在上一篇文章中,楼主介绍了在微信公众号开发过程中的接入概述,同时实现了接入概述中的第一步,服务器相关配置,同时做了穿透测试,实现了外网用户访问本地应用服务的可行性。
本篇文章将介绍接入概述中的第二步,验证消息的确来自微信服务器。
在开发文档中,对第二步的验证,微信官方文档给出了这样的描述,楼主这里直接把它贴过来。
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
具体实现代码如下所示:
package service;
import java.security.MessageDigest;
import java.util.Arrays;
public class WxService {
private static final String TOKEN = "wxtest1";
/**
* 签名验证
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean check(String signature, String timestamp, String nonce) {
/**
* 签名验证规则
* 1)将token、timestamp、nonce三个参数进行字典序排序
* 2)将三个参数字符串拼接成一个字符串进行sha1加密
* 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
*/
//将token、timestamp、nonce三个参数进行字典序排序
String[] str = new String[]{TOKEN, timestamp, nonce};
Arrays.sort(str);
//将三个参数字符串拼接成一个字符串进行sha1加密
String str1 = str[0] + str[1] + str[2];
String encodeSignature = sha1(str1);
//开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
return encodeSignature.equalsIgnoreCase(signature);
}
/**
* sha1加密
*/
public 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 builder = new StringBuilder();
//处理加密结果
for(byte b:digest) {
//高4位与15相与
builder.append(chars[(b>>4)&15]);
//低4位直接相与
builder.append(chars[b&15]);
}
return builder.toString();
}catch(Exception e) {
System.out.println("异常类型为:" + e);
}
return null;
}
}
在处理逻辑过程中添加校验方法的调用,具体代码如下所示:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import service.WxService;
/**
* Servlet implementation class WxServlet
*/
@WebServlet("/wx")
public class WxServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public WxServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
*
* 参数 描述
* signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
* timestamp 时间戳
* nonce 随机数
* echostr 随机字符串
*
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// response.getWriter().append("Served at: ").append(request.getContextPath());
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(WxService.check(signature, timestamp, nonce)) {
// System.out.println("校验成功");
PrintWriter writer = response.getWriter();
writer.print(echostr);
writer.flush();
writer.close();
}else {
System.out.println("校验失败");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// doGet(request, response);
System.out.println("post");
}
}
调用完成之后开始启动测试,测试结果如下所示:
控制台返回的结果如下所示: