微信平台的token安全验证(转)

gcttong00 2014-03-27

本文目标:学习一种比较安全的服务器间互相验证身份的方式。

问题:开发微信公众平台接口,开发者的服务器为了确保请求是否来自微信服务器,应该如何去做?

1)在微信管理页面上填写URL和TOKEN,开发者服务器上也记录同样的TOKEN。

2)微信服务器发送HTTP请求,附带上参数(注意TOKEN是不会被传输的)

参数描述

signature微信加密签名

timestamp时间戳

nonce随机数

echostr随机字符串

其中signature值通过如下摘要运算得出:

1.将token、timestamp、nonce三个参数进行字典序排序

2.将三个参数字符串拼接成一个字符串进行sha1加密(这个加密是不可逆的),并将结果的byte[]转换为16进制字符串

3)开发者服务器接收到signature,timestamp,nonce,echostr参数,跟服务器做同样的摘要运算,得到预期的一个signatrue,然后对比微信服务器发送过来的signature参数,如果相同,证明双方的TOKEN是一致的,开发者服务器确实接收到了来自微信服务器的请求,开发者服务器最后返回echostr,以告诉微信服务器接入成功。具体的开发者服务器校验逻辑代码如下显示。

Java代码收藏代码

packagemessage;

importjava.security.*;

importjava.util.Arrays;

/***

*微信消息接口认证token摘要类

*

*这个摘要类实现为单例,校验一个签名是否合法的例子如下

*<pre>

*WeixinMessageDigestwxDigest=WeixinMessageDigest.getInstance();

*booleanbValid=wxDigest.validate(signature,timestamp,nonce);

*</pre>

*

*

*@authorliguocai

*/

publicfinalclassWeixinMessageDigest{

/**

*单例持有类

*@authorliguocai

*

*/

privatestaticclassSingletonHolder{

staticfinalWeixinMessageDigestINSTANCE=newWeixinMessageDigest();

}

/**

*获取单例

*@return

*/

publicstaticWeixinMessageDigestgetInstance(){

returnSingletonHolder.INSTANCE;

}

privateMessageDigestdigest;

privateWeixinMessageDigest(){

try{

digest=MessageDigest.getInstance("SHA-1");

}catch(Exceptione){

thrownewInternalError("initMessageDigesterror:"+e.getMessage());

}

}

/**

*将字节数组转换成16进制字符串

*@paramb

*@return

*/

privatestaticStringbyte2hex(byte[]b){

StringBuildersbDes=newStringBuilder();

Stringtmp=null;

for(inti=0;i<b.length;i++){

tmp=(Integer.toHexString(b[i]&0xFF));

if(tmp.length()==1){

sbDes.append("0");

}

sbDes.append(tmp);

}

returnsbDes.toString();

}

privateStringencrypt(StringstrSrc){

StringstrDes=null;

byte[]bt=strSrc.getBytes();

digest.update(bt);

strDes=byte2hex(digest.digest());

returnstrDes;

}

/**

*校验请求的签名是否合法

*

*加密/校验流程:

*1.将token、timestamp、nonce三个参数进行字典序排序

*2.将三个参数字符串拼接成一个字符串进行sha1加密

*3.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

*@paramsignature

*@paramtimestamp

*@paramnonce

*@return

*/

publicbooleanvalidate(Stringsignature,Stringtimestamp,Stringnonce){

//1.将token、timestamp、nonce三个参数进行字典序排序

Stringtoken=getToken();

String[]arrTmp={token,timestamp,nonce};

Arrays.sort(arrTmp);

StringBuffersb=newStringBuffer();

//2.将三个参数字符串拼接成一个字符串进行sha1加密

for(inti=0;i<arrTmp.length;i++){

sb.append(arrTmp[i]);

}

StringexpectedSignature=encrypt(sb.toString());

//3.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

if(expectedSignature.equals(signature)){

returntrue;

}

returnfalse;

}

privateStringgetToken(){

return"111111";

}

publicstaticvoidmain(String[]args){

Stringsignature="f86944503c10e7caefe35d6bc19a67e6e8d0e564";//加密需要验证的签名

Stringtimestamp="1371608072";//时间戳

Stringnonce="1372170854";//随机数

WeixinMessageDigestwxDigest=WeixinMessageDigest.getInstance();

booleanbValid=wxDigest.validate(signature,timestamp,nonce);

if(bValid){

System.out.println("token验证成功!");

}else{

System.out.println("token验证失败!");

}

}

}

4)这个摘要对比的技术,同样适用于单点登录、服务期间互相调用的身份验证,前提是每台服务器都持有相同的TOKEN。此外,有些细节可以优化,例如通过timestamp对签名做超时的处理,超时的签名默认不通过;请求的参数可以加上IP,USERID等额外信息;返回的echostr可以再次与TOKEN做摘要,可以使微信服务器确保接受来自开发者服务器的响应,但是微信服务器没有这么做,也许它本身已经做了足够安全控制。

微信消息接口文档:

http://mp.weixin.qq.com/wiki/index.php?title=%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3%E6%8C%87%E5%8D%97

相关推荐