刚开发完微信授权接入,这里做个分享,供大家参考。
授权流程可以参照微信官方api,流程图如下:
1.准备工作
导入微信官方的加解密算法(下载地址),之后要做:
(1) 在java官方网站下载JCE无限制权限策略文件(JDK7的下载地址: * http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html)
下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt。
如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件。
如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件。
(2) 修改解密算法。由于微信发来的XML消息体中可能包含ToUserName或AppId,而微信提供的解密算法中的XML节点只有ToUserName一种形式。所以需要修改XMLParse类中的extract方法:
- /**
- * 提取出xml数据包中的加密消息
- *
- * @param xmltext 待提取的xml字符串
- * @param element xml中参数名称 可选 "ToUserName","AppId"(自己创建的枚举AnotherElement,有ToUserName和AppId俩个成员)
- * @return 提取出的加密消息字符串
- * @throws AesException
- */
- public static Object[] extract(String xmltext, WXBizMsgCrypt.AnotherElement element) 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(element.name());
- 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.流程图代码解释
①⑨授权事件URL接收到微信的请求,包括ticket推送和授权消息通知:
- @Override
- public void handleWechatEventPush (HttpServletRequest request){
- String timestamp = request.getParameter("timestamp");
- String nonce = request.getParameter("nonce");
- String msgSignature = request.getParameter("msg_signature");
- StringWriter writer = new StringWriter();
- try {
- //获得微信发来的加密消息
- IOUtils.copy(request.getInputStream(), writer, "UTF-8");
- String fromXML = writer.toString();
- WXBizMsgCrypt pc = new WXBizMsgCrypt(componentToken, encodingAESKey, componentAppId);
- //获得解密后的XML消息体
- String result = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML, WXBizMsgCrypt.AnotherElement.AppId);
- LOGGER.info(" authorize decrypt result {}", result);
- Map<String, String> map = MessageUtil.parseXml(result);
- //消息类型
- String infoType = map.get("InfoType");
- switch (InfoType.valueOf(infoType)) {
- //授权成功,可以获得授权码;授权码也可以在流程图⑤中获得,所以可以忽略
- case authorized:
- break;
- //取消授权,可以删除本地保存的已授权公众号
- case unauthorized:
- String appId = map.get("AuthorizerAppid");
- //todo 删除本地授权的公众号
- break;
- //更新授权,可以更新授权方令牌authorizer_access_token,刷新令牌authorizer_refresh_token,权限集列表等
- case updateauthorized:
- //todo 更新令牌等
- break;
- //推送ticket,妥善保存ticket,用于获取component_access_token
- case component_verify_ticket:
- String ticket = map.get("ComponentVerifyTicket");
- //存储ticket
- break;
- default:
- break;
- }
- } catch (Exception e) {
- LOGGER.error(e.toString(), e);
- }
- }
⑤点击链接后进入授权页如下(切记要配置测试ip地址和保证pre_auth_code有效):完成授权后,会进入redirect_url,可以从url中获取authorization_code和过期时间
⑥⑦⑧获得authorizer_access_token和authorizer_refresh_token。authorizer_access_token和开发微信公众平台的access_token功能一致,可以调用菜单设置等接口;另外还可以获得公众号的基本信息和其他选项设置。
3.代公众号实现消息处理
授权成功后,微信会将消息和事件通知发送到公众号消息与事件接收URL上,与公众号开发者模式不同的是,第三方处理消息和事件通知,需要对数据进行加解密。代码如下:
- @Override
- public String thirdPlatformPrecessRequest(HttpServletRequest request, String appId) {
- String timestamp = request.getParameter("timestamp");
- String nonce = request.getParameter("nonce");
- String msgSignature = request.getParameter("msg_signature");
- StringWriter writer = new StringWriter();
- try {
- IOUtils.copy(request.getInputStream(), writer, "UTF-8");
- String fromXML = writer.toString();
- WXBizMsgCrypt pc = new WXBizMsgCrypt(componentToken, encodingAESKey, componentAppId);
- String result = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML, WXBizMsgCrypt.AnotherElement.ToUserName);//这里传入的是ToUserName,参见上面的准备工作
- Map<String, String> resultMap = MessageUtil.parseXml(result);
- String respMessage = getRespMessage(resultMap);
- return pc.encryptMsg(respMessage, timestamp, nonce);
- } catch (Exception e) {
- LOGGER.error(e.toString(), e);
- return "";
- }
- }