使用java开发微信公众号
注意:
我们都知道学习使用只能申请【订阅号】,但是,我们申请的是个人(没有认证)。这就造成了一下问题:由于用户体验和安全性方面的考虑,微信公众号的注册有一定门槛,某些高级接口的权限需要微信认证后才可以获取.所以,为了帮助开发者快速了解和上手微信公众号开发,熟悉各个接口的调用,我们推出了微信公众帐号测试号,通过手机微信扫描二维码即可获得测试号。
1、测试号的申请
登录到微信公众平台:https://mp.weixin.qq.com/,打开【订阅号】中的开发文档。
选择 《开始开发》-->《接口测试号申请》-----点击------ > 《进入微信公众测试号申请系统》
2、填写服务器配置
申请成功后的页面,此页面需要两个参数,url和token
URL:填写的URL(本地项目地址,需要自己创建,见后文3、项目创建)
需要正确响应微信发送的Token验证
Token: 暂时可以随便设置
注意:我们要想微信消息响应到本地,就必须使用内网穿透。简单概述:通过内网穿透别人就可以访问我们本地的项目(见后文4内网穿透工具使用)。
3、项目创建
用Eclipse新建一个项目,这里我建的是web项目
新建一个servlet,重写doGet方法接收微信发来的GET请求
package com.bgm.bgmserver.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.bgm.bgmserver.service.BGMService;
public class BGMServlet extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("微信发送的get请求");
//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
String signature=request.getParameter("signature");
//时间戳
String timestamp=request.getParameter("timestamp");
//随机数
String nonce=request.getParameter("nonce");
//随机字符串
String echostr=request.getParameter("echostr");
System.out.println("signature:"+signature);
System.out.println("timestamp:"+timestamp);
System.out.println("nonce:"+nonce);
System.out.println("echostr:"+echostr);
//校验请求
if(BGMService.check(signature,timestamp,nonce)){
System.out.println("接入成功");
//若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效
PrintWriter out = response.getWriter();
out.print(echostr);
out.flush();
out.close();
}else{
System.out.println("接入失败");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("微信发送的post请求");
//输出xml数据包
/*ServletInputStream is = request.getInputStream();
byte[] b=new byte[1024];
int len;
StringBuilder sb=new StringBuilder();
while((len=is.read(b))!=-1){
sb.append(new String(b,0,len));
}
System.out.println(sb.toString());*/
Map<String,String> requestMap= BGMService.parseRequest(request.getInputStream());
System.out.println(requestMap);
}
}
BGMService 代码:
package com.bgm.bgmserver.service;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class BGMService {
/**
* Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
* 比如这里我将Token设置为bgm_tocken
*/
private static String TOKEN="bgm_tocken";
/**
* @param timestamp 时间戳
* @param nonce 随机数
* @param signature 微信加密签名
* @return
*/
public static boolean check(String signature, String timestamp, String nonce) {
// 1)将token、timestamp、nonce三个参数进行字典序排序
String[] strArray = {TOKEN, timestamp, nonce};
Arrays.sort(strArray);
//2)将三个参数字符串拼接成一个字符串进行sha1加密
StringBuilder sb = new StringBuilder();
for (String str : strArray) {
sb.append(str);
}
//sha1加密
String sha1Str=sha1(sb.toString());
System.out.println("sha1加密:"+sha1Str);
System.out.println("signature:"+signature);
//3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信//校验签名
return sha1Str.equalsIgnoreCase(signature);
}
/**
* 进行sha1加密
* @param str
* @return
*/
private static String sha1(String str) {
try {
//获取一个加密对象
MessageDigest md=MessageDigest.getInstance("sha1");
//加密
byte [] digest=md.digest(str.getBytes());
char [] chars= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
StringBuilder sb=new StringBuilder();
//处理加密结果
for(byte b:digest){
sb.append(chars[(b>>4)&15]);
sb.append(chars[b&15]);
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* 将字符串进行sha1加密
*
* @param str 需要加密的字符串
* @return 加密后的内容
*/
/* public String sha1(String str) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes());
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}*/
/*public static String getSha1(String str){
if(str==null||str.length()==0){
return null;
}
char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char buf[] = new char[j*2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}*/
/**
* 解析xml数据包
* @param is
* @return
*/
public static Map<String, String> parseRequest(InputStream is) {
Map<String, String> map=new HashMap<String, String>();
SAXReader reader = new SAXReader();
try {
//读取数据流,获取文档对象
Document document=reader.read(is);
//根据文档对象获取根节点
Element root = document.getRootElement();
//获取根节点的所有的子节点
List<Element> elements = root.elements();
for (Element e : elements) {
map.put(e.getName(), e.getStringValue());
}
} catch (DocumentException e) {
e.printStackTrace();
}
return map;
}
}
其中SAXReader 需要引入dom4j-1.6.1.jar jaxen-1.1.1.jar
代码写完后,启动项目,需要一个工具将我们的内网链接映射为公网,这样微信才能访问到我们的后台,这里我采用的是一款免费的映射工具ngrok
4、内网穿透工具使用
内网穿透工具(外网映射工具)ngrok:将我们的内网链接映射为公网。下载链接: https://ngrok.com/download
下载完后解压到指定位置
双击运行(ngrok.exe),弹出命令行黑窗口
输入ngrok http 8080 回车,显示结果中,Forwarding对应内容即为公网链接,可直接访问本机127.0.0.1:8080下的链接内容,分别是http协议和https协议对应的地址:
5、测试
执行步骤2,填入地址,与自定义的tocken,此处为bgm_tocken,点击提交
java后台显示:
此时接口接入成功: