微信开发文档写得不够详细,而且跳屏比较严重,对于整天忙于开发,并且第一次接触的小伙伴来说,真的是太痛苦了,本人就是这样过来的,并且看了很多网上的材料,最后意识到,是自己对于这种开发文档接触的太少,研读的不够精细导致的,苦笑...
今天任务:第三方平台方获取预授权码(pre_auth_code)
步骤1:第三方平台方获取预授权码(pre_auth_code)
预授权码是第三方平台方实现授权托管的必备信息,可以通过本文第3部分获取。(——摘自微信开发文档)
获取预授权码pre_auth_code
该API用于获取预授权码。预授权码用于公众号或小程序授权时的第三方平台方安全验证。
接口调用请求说明
http请求方式: POST(请使用https协议)
https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=xxx
POST数据示例:
{
"component_appid":"appid_value"
}
请求参数说明
参数 | 说明 |
---|---|
component_appid | 第三方平台方appid |
返回结果示例
{"pre_auth_code":"Cx_Dk6qiBE0Dmx4EmlT3oRfArPvwSQ-oa3NL_fwHM7VI08r52wazoZX2Rhpz1dEw","expires_in":600}
结果参数说明
参数 | 说明 |
---|---|
pre_auth_code | 预授权码 |
expires_in | 有效期,为10分钟 |
很多人到这步,看得云里雾里,不知道怎么获取component_access_token,其实component_access_token需要通过微信推送过来的消息间接的获取,也就是前文提交审核材料中的授权事件接收URL这个你提交材料中所设置的路径,微信服务器会推送一个票据component_verify_ticket,通过这个票据再去获取component_access_token。
此处给出我的实现方法
此方法就是授权事件接收URL接受component_verify_ticket操作,因为微信服务端推送的是xml的加密文件,所以需要我们自己去解析,给出微信提供的各种语言demo,点击示例下载就可获取。
@RequestMapping(value = "/wx/sysMsgCallback")
@ResponseBody
public ResponseEntity<String> wechatSysMsgCallback(HttpServletRequest request, HttpServletResponse response,
@RequestBody String xml, @RequestParam(value = "signature", required = false) String signature,
@RequestParam(value = "timestamp", required = false) String timeStamp,
@RequestParam(value = "nonce", required = false) String nonce,
@RequestParam(value = "encrypt_type", required = false) String encryptType,
@RequestParam(value = "msg_signature", required = false) String msgSignature) throws Exception {
logger.info("解密前明文:" + xml);
logger.info("signature = " + signature + " timeStamp =" + timeStamp + " nonce = " + nonce + " msgSignature =" + msgSignature);
String encodingAesKey = "***"; // 加解密秘钥,自行定义
String token = "***"; // 加解密token,自行定义
String appId = "***"; // 第三方平台appid,提交申请第三方平台时微信服务器分发的appid
// 微信提供的源代码
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
// 第三方收到公众号平台发送的消息
String result2 = "";
if(xml.contains("ToUserName")) {
result2 = pc.decryptMsgTwo(msgSignature, timeStamp, nonce, xml);
} else if(xml.contains("AppId")) {
result2 = pc.decryptMsg(msgSignature, timeStamp, nonce, xml);
}
System.out.println("解密后明文: " + result2);
logger.info("【success】 =" + result2);
// 微信每10分钟推送给我们的ticket
this.saveTicketToRedis(result2);
response.getWriter().write("success");
response.getWriter().flush();
response.getWriter().close();
return null;
}
给出自己修改的两个解密操作
1、decryptMsg
public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
throws AesException {
// 密钥,公众账号的app secret
// 提取密文
Object[] encrypt = XMLParse.extract(postData);
// 验证安全签名
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());
// 和URL中的签名比较是否相等
if (!signature.equals(msgSignature)) {
throw new AesException(AesException.ValidateSignatureError);
}
// 解密
String result = decrypt(encrypt[1].toString());
return result;
}
public static Object[] extract(String xmltext) throws AesException {
Object[] result = new Object[3];
try {
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(xmltext);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
// 修改后的代码
NodeList nodelist2 = root.getElementsByTagName("AppId");
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);
}
}
2、decryptMsgTwo
public String decryptMsgTwo(String msgSignature, String timeStamp, String nonce, String postData)
throws AesException {
// 密钥,公众账号的app secret
// 提取密文
Object[] encrypt = XMLParse.extractTwo(postData);
// 验证安全签名
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());
// 和URL中的签名比较是否相等
if (!signature.equals(msgSignature)) {
throw new AesException(AesException.ValidateSignatureError);
}
// 解密
String result = decrypt(encrypt[1].toString());
return result;
}
public static Object[] extractTwo(String xmltext) throws AesException {
Object[] result = new Object[3];
try {
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(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);
}
}
注意:解析报异常java.security.InvalidKeyException:illegal Key Size的解决方案
在官方网站下载JCE无限制权限策略文件(JDK6的下载地址)(JDK7的下载地址)(JDK8的下载地址)
下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt,如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件;如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件
掉坑1:
解析出来获取的component_verify_ticket是一串带有@@@的字符串,需要自行将@前面不要的信息截取掉,很多人在这里被坑的很惨。
通过以下这个接口获取component_access_token
http请求方式: POST(请使用https协议)
https://api.weixin.qq.com/cgi-bin/component/api_component_token
POST数据示例:
{
"component_appid":"appid_value" ,
"component_appsecret": "appsecret_value",
"component_verify_ticket": "ticket_value"
}