最近开始搞搞开放平台,先是TaoBaoSDK,挺好用的基本每碰到什么问题,就是各种没权限。。。
后来搞微信的,第一步卡了我一天。。。年纪大了,反应迟钝啊。。。
首先按照公众号平台里把信息配置好,这里我就不截图了
服务端刚开始调用的是Demo里WXBizMsgCrypt.verifyUrl(msgSignature,timeStamp,nonce,echoStr)
不要问我为什么,因为懒。。。
就是验不通过,经过核对参数、设置编码等等最后发现这个方法不是用来响应服务端验证的,而且Demo里似乎也没有现成的方法。。。。
于是动手开始改了(同学们要相信自己啊):
签名方法:考虑到Demo里那个方法是四个参数的,所以写了一个兼容多个参数的
/**
 * 签名数据
 * @param args 签名数据列表 arg[0]必须为appId
 * @return 签名结果
 */
public String sign(String... args){
    /*** 不用看 begin,就是把第一个参数由appId换成token,因为我这个多账号,否则写死 ****/
    String appId = args[0];
    AppConf conf = getAppConf(appId);
    args[0]=conf.getToken();
    /*** 不用看 end  *****/
    // 字典序排序,如果有null的这里会报错
    Arrays.sort(args);
    // 字符串拼接
    StringBuffer buffer = new StringBuffer();
    for(String str:args){
        if(str!=null){
            buffer.append(str);
        }
    }
    try{
        // SHA1签名生成
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(buffer.toString().getBytes());
        byte[] digest = md.digest();
        StringBuffer hexstr = new StringBuffer();
        String shaHex = "";
        for (int i = 0; i < digest.length; i++) {
            shaHex = Integer.toHexString(digest[i] & 0xFF);
            if (shaHex.length() < 2) {
                hexstr.append(0);
            }
            hexstr.append(shaHex);
        }
        return hexstr.toString();
    }catch(Exception ex){
        throw new BaseRuntimeException(WXServerError.sign(appId,args));
    }
    
}
调用的地方:
- 签名返回报文,4个参数(不知道是不是这个,反正签了):
String sign =wxMsgCryptService.sign(info.getAppId(),timestamp,nonce,encrypt);
- 响应服务端验证,3个参数(这个是正主):
if(signature!=null){
    String sign = wxMsgCryptService.sign(appId,timestamp,nonce);
    
    if(sign.equals(signature)){
        if(body==null){
            return echostr;
        }
    }else{
        throw new BaseRuntimeException(WXServerError.verify(appId,timestamp,nonce,signature));
    }
}
返回部分:返回是我很费解的地方,可以是字符串、可以是xml、没有异常报文不是200的一律失败,不过好像也没什么不对。
// 返回的直接是Http Body了
@ResponseBody
/** 我习惯了用扩展名来区分响应,微信那边配的是server/{appId}.xml
 *  这样返回的就是xml了,那天支持json了换json吧
 */
@RequestMapping("server/{appId}.*")
public Object service(
    @PathVariable("appId") String appId,
    @RequestParam(value="signature",required=false)String signature,
    @RequestParam(value="timestamp",required=false)String timestamp,
    @RequestParam(value="nonce",required=false)String nonce,
    @RequestParam(value="echostr",required=false)String echostr,
    @RequestBody(required=false) String body){
    logger.info("accept -> signature:{},timestamp:{},nonce:{},echostr:{}",new Object[]{
            signature,timestamp,nonce,echostr
    });
    // 配置的时候有三个选项:明文的时候就不验签了,估计不会有人这么配置吧
    // 接受明文消息时,不进行验签
    if(signature!=null){
        String sign = wxMsgCryptService.sign(appId,timestamp,nonce);
        
        if(sign.equals(signature)){
            if(body==null){
                // 响应验证到这里就结束了,返回echostr,微信那边就验证通过了
                return echostr;
            }
        }else{
            throw new BaseRuntimeException(WXServerError.verify(appId,timestamp,nonce,signature));
        }
    }
    
    logger.info("receive->{}",body);
    
     
    // 以下接受到消息的处理,属于增值包 😜
    
    MsgCryptInfo info = new MsgCryptInfo();
    info.setAppId(appId);
    info.setNonce(nonce);
    info.setSignature(signature);
    info.setTimeStamp(timestamp);
    // 里面判断要不要解密消息,不要直接返回了
    WXMessage message = wxMessageService.getMessage(info,body);
    // 去找对应的消息处理器生成返回消息
    message = wxMessageService.reply(message);
    // 无返回消息时
    if(message==null){
        // 官方说返回"success"字串也是成功,我是不会让它走到这个分支的,邪恶啊
        return DEFAULT_REPLY;
    }
    // 接受明文消息时,不进行验签
    if(signature!=null){
        // 消息加密部分,官方的Demo抄下来就好了
        message = wxMessageService.encryptMessage(info, message);
    }
    
    return message;
}
PS:当时我用verifyURL跑不过验证的时候,我内心时崩溃的,在想难道未认证的订阅号连这个都不让干了。所以我终究还是低估了微信对于个人开发者的友好
 
  
  
  
 
 
 