##請求校驗流程
###簡介
我們要開發的請求校驗程序必須能夠處理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.hmoore.net/yongxin/wxapi/195098)
2. 創建一個Servlet類,用于接收GET請求傳遞的4個參數,并調用SignUtil 工具類中封裝的checkSignature()方法進行請求校驗,如果校驗成功,則將接收到的參數echostr原樣返回。代碼如下:
[SignServlet.java](http://www.hmoore.net/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