微信公众号第三方授权——接受微信推送component_verify_ticket协议
作者:凌晨四点的newyork
接收component_verify_ticket
在第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket。第三方平台方在收到ticket推送后也需进行解密(详细请见【消息加解密接入指引】),接收到后必须直接返回字符串success。
一、接受到微信推送的消息参数值如下
/**
* @title 微信端定时通知ticket接收
* @param timestamp
* @param nonce
* @param msgSignature
* @return
*/
@RequestMapping(value = "/ticket", produces = "application/json; charset=utf-8")
public String notifyWxTicket(@RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce,
@RequestParam("msg_signature") String msgSignature, @RequestBody String postData)
参数说明:timestamp时间戳;nonce随机字符串;msg_signature签名串;postData即xml数据内容。
postData数据示例:
<xml>
<AppId><![CDATA[AppId]]></AppId>
<Encrypt><![CDATA[加密XML]]></Encrypt>
</xml>
二、解密xml数据内容
下载官方demo
找到java的文件夹
在项目中的pom文件,添加如下依赖:
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<!--解密jar包-->
<dependency>
<groupId>com.aes</groupId>
<artifactId>aes-jre</artifactId>
<version>1.6</version>
</dependency>
注意:此处引入微信第三方的jar包需要使用maven引入本地jar包。
使用微信第三方的jar包提供的方法对xml数据解密
将参数换成上面接受到的参数数据
/**
* 对微信推送消息进行解密,获取ComponentVerifyTicket
*
* @param timestamp
* @param nonce
* @param msgSignature
* @param postData
* @return
*/
public Map<String, String> Decrypt(String timestamp, String nonce, String msgSignature, String postData)
throws Exception {
log.info("token: " + token);
log.info("encodingAesKey: " + encodingAesKey);
log.info("appId: " + appId);
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
String requestXml = pc.decryptMsg(msgSignature, timestamp, nonce, postData);
log.info("解密后明文是: " + requestXml);
Map<String, String> xmlMap = XMLParser.getMapFromXML(requestXml);//xml格式数据转map
return xmlMap;
}
高能预警,如下内容,皆为真实亲测,微信第三方jar包中存在一部分坑…
下面是jar包中的源码,对于上面提到的密文postData的提取代码:(箭头表示------点进去)
/**
* 提取出xml数据包中的加密消息
* @param xmltext 待提取的xml字符串
* @return 提取出的加密消息字符串
* @throws AesException
*/
public static Object[] extract(String xmltext) throws AesException {
Object[] result = new Object[3];
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(xmltext);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
NodeList nodelist2 = root.getElementsByTagName("ToUserName");
result[0] = 0;
result[1] = nodelist1.item(0).getTextContent();
result[2] = nodelist2.item(0).getTextContent();
return result;
} catch (Exception e) {
e.printStackTrace();
throw new AesException(AesException.ParseXmlError);
}
}
注意看这五行代码
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
NodeList nodelist2 = root.getElementsByTagName("ToUserName");
result[0] = 0;
result[1] = nodelist1.item(0).getTextContent();
result[2] = nodelist2.item(0).getTextContent();
与上面贴出的postData数据示例做比对,发现微信推送过来的xml数据包中并没有ToUserName这个标签。说明一下,这个ToUserName实际上是一个微信号,在此处微信并没有推送,所以只需把上边这五行代码中跟ToUserName有关的代码去掉即可,与此同时,需要去掉如下图中的两个异常,否则会一直发生异常。
贴一个我解密后的数据示例:
<xml>
<AppId><![CDATA[wx6***********26]]></AppId>
<CreateTime>1557371567</CreateTime>
<InfoType><![CDATA[component_verify_ticket]]></InfoType>
<ComponentVerifyTicket><![CDATA[ticket@@@*******************]]></ComponentVerifyTicket>
</xml>
需要提取出ComponentVerifyTicket这个的值,ComponentVerifyTicket的有效时间为12小时,建议保存最近可用的component_verify_ticket,提供后续获取第三方component_access_token时使用,保存方式自选,可以是数据库,缓存,或静态类等。