##请求校验流程
###简介
我们要开发的请求校验程序必须能够处理HTTP GET 请求,而且要对请求者进行身份校验,以确保请求来自微信服务器,请求校验流程如下:
1. 获取HTTP GET请求中的4个参数:signatrue、timestamp、nonce和echostr;
2. 将token、timestamp和nonce 3个参数按**字典序**排序;
3. 将排序后的3个参数按顺序拼接成一个字符串,并对该字符串进行sha1 加密;
4. 将sha1 加密后的字符串与参数signature进行对比,如果相等则证明该请求来自微信服务器。
* * * * *
######**提示**:步骤2提到的参数token 不是从GET请求中获取的,而是由开发者指定的。在启用开发者模式时,也会要求填写token,这两处token要求保持一致
* * * * *
###请求校验流程分析
在Java中,处理HTTP GET请求就需要用到Servlet(也可以使用JSP或者Struts,但本质也是Servlet),在Servlet中 接收参数 signature、timestamp、nonce和echostr,代码如下所示
~~~
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
~~~
要对token、timestamp、和nonce 3个参数按照 字典序 排序,可以使用java.util.Arrays类的sort()方法;而将排序后的结果拼接成一个字符串,可以使用String类的contact()方法,实现代码如下:
~~~
//对token、timestamp和nonce排序
String[] paramArr = new String[] {token,timestamp,nonce};
Arrays.sort(paramArr);
//将排序后的结果拼接成一个字符串
String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
~~~
对拼接后的字符串content进行sha1加密可以使用java.security.MessageDigest类来实现,代码如下:
~~~
MessageDigest md = MessageDigest.getInstance("SHA-1");
//对拼接后的字符串进行sha1加密
byte[] digest = md.digest(content.getBytes());
~~~
可以看到,进行sha1加密后的结果是一个byte数组,而我们需要的是一个字符串。所以还需要通过下面的方法将byte数组转换为字符串:
~~~
/**
* 将字节数组转换为十六进制字符串
* @param bytearray
* @return
* /
private static String byteToStr(byte[] bytearray)
{
String strDigest = "";
for (int i=0;i<bytearray.length;i++)
{
strDigest += byteToHexStr(bytearray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
* @param ib
* @return
* /
private static String byteToHexStr(byte ib)
{
char[] Digit = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] ob = new char[2];
ob[0] = Digit[(ib >>> 4) & 0X0F];
ob[1] = Digit[ib & 0X0F];
String s = new String(ob);
return s;
}
~~~
调用上面的 byteToStr()方法将sha1加密后的byte 数组转换成字符串,如下所示:
~~~
String ciphertext = byteToStr(digest);
~~~
最后,还要讲字符串ciphertext 与参数signature进行比较,如果相等,则将参数echostr原样返回,代码如下
~~~
PrintWriter out = response.getWriter();
//请求校验,若校验成功则原样返回echostr
if(ciphertext.equals(signature.toUpperCase())){
out.print(echostr);
}
out.close();
~~~
###请求校验程序实现和封装
1. 首先,新建一个名为 test 的Web项目,在项目的src下创建一个普通Java类SignUtil,该类对请求校验流程的实现进行了封装,方便在Servlet中调用。代码如下:
[SignUtil.java](http://www.kancloud.cn/yongxin/wxapi/195098)
2. 创建一个Servlet类,用于接收GET请求传递的4个参数,并调用SignUtil 工具类中封装的checkSignature()方法进行请求校验,如果校验成功,则将接收到的参数echostr原样返回。代码如下:
[SignServlet.java](http://www.kancloud.cn/yongxin/wxapi/195105)
3. Servlet创建完成后还需要在web.xml中进行配置,配置如下
~~~
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>signServlet</servlet-name>
<servlet-class>
test.liu.sign.servlet.SignServlet
</servlet-class>
</servlet>
<!-- /coreServlet用于指定该Servlet的访问路径 -->
<servlet-mapping>
<servlet-name>signServlet</servlet-name>
<url-pattern>/signServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
~~~
在web.xml中需要特别注意的是 <url-pattern> 元素,它指定了SignServlet 的访问路径。例如,将项目部署到本机端口8080的tomcat下,根路径指的是 localhost:8080/test/,SignServlet的访问路径就是 localhost:8080/test/SignServlet