在第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket。第三方平台方在收到ticket推送后也需进行解密(详细请见【消息加解密接入指引】),接收到后必须直接返回字符串success。
1. 获取component_verify_ticket接口配置
接口名要与微信开发平台第三方平台上配置的"授权事件接收URL"接口名对应。
例:getComponentVerifyTicket
2. 写接口
/**
* 接收component_verify_ticket 或 authorized事件
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@ApiOperation(value = "接收component_verify_ticket 或 authorized事件", notes = "接收component_verify_ticket 或 authorized事件", response = String.class)
@RequestMapping(value = "getComponentVerifyTicket")
public void getComponentVerifyTicket(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("接收component_verify_ticket 或 authorized事件");
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");
String msgSignature = request.getParameter("msg_signature");
StringBuilder sb = new StringBuilder();
BufferedReader in = request.getReader();
String line;
while((line = in.readLine()) != null) {
sb.append(line);
}
String postData = sb.toString();
logger.info("nonce: " + nonce);
logger.info("timestamp: " + timestamp);
logger.info("msgSignature: " + msgSignature);
logger.info("postData: " + postData);
thirdPartyService.getComponentVerifyTicket(timestamp, nonce, msgSignature, postData);
responseUtil(response, "success");
// return "success";
}
@Override
public void getComponentVerifyTicket(String timestamp, String nonce, String msgSignature, String postData) throws Exception {
// 需要加密的明文
WXBizMsgCrypt pc = new WXBizMsgCrypt(TOKEN, ENCODING_AES_KEY, THIRD_PARTY_APP_ID);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(postData);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
String encrypt = nodelist1.item(0).getTextContent();
String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";
String fromXML = String.format(format, encrypt);
String result = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);
logger.info("解密后: " + result);
//获取ComponentVerifyTicket
Map<String, String> xmlMap = xmlToMap(result);
String componentVerifyTicket = xmlMap.get("ComponentVerifyTicket");
if(StringUtils.isNotBlank(componentVerifyTicket)) {
//获取ticket,没有则为authorized事件
RedisUtils.set("ComponentVerifyTicket", componentVerifyTicket);
logger.info("ComponentVerifyTicket: " + componentVerifyTicket);
}
}
3. 坑
a. 需要注意的是这个接口会有两种数据形式访问,一个是component_verify_ticket,解密后有ComponentVerifyTicket这个参数,另一个是authorized事件,解密后没有ComponentVerifyTicket这个参数,因此在获取componentVerifyTicket时要加个判断。
b. 同时需要注意的是,解密时要加上<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>
原因是官方的demo文件里,类XMLParse.java里,提取出xml数据包中的加密消息这个方法(extract(String xmltext))有一步需要获取字段ToUserName,
而原数据没有这个字段,所以或报异常。
c. 在全网发布检测时“组件ticket正确接收”一直失败但日志显示无异常的原因:方法要返回’success‘,并且不能直接return 'success',要使用response输出’success‘。
注:
1. TOKEN , ENCODING_AES_KEY, THIRD_PARTY_APP_ID 都是第三方的配置参数
其中TOKEN为消息校验Token, ENCODING_AES_KEY为消息加解密Key, THIRD_PARTY_APP_ID 为第三方平台的app_id
2.注意要把自己测试的机子的ip在自己的微信开发平台第三方平台里加入白名单
3. 测试
登入微信开放平台https://open.weixin.qq.com/
登入账号进入第三方平台,点击全网发布
点击确认后可进行测试。
参考文档:授权流程技术说明 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=