Java解析并处理微信公众号回调数据

原创     发表于 2020-11-14 20:23     阅读 84  

微信用户在关注、取消关注、点击菜单等相关操作时,如果配置了回调接口,微信会将这些事件信息回传到开发者配置指定的URL地址。微信公众号的文档是这么解释的:

在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,详细内容如下:

1 关注/取消关注事件

2 扫描带参数二维码事件

3 上报地理位置事件

4 自定义菜单事件

5 点击菜单拉取消息时的事件推送

6 点击菜单跳转链接时的事件推送

参考地址:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html


微信回调的作用显而易见,比如公众号用户统计、网站扫码登录、注册后保存微信信息作为消息推送等。要启用回调功能,得先登录公众号配置开发者信息。这里有需要注意的地方,配置保存的时候,https访问的URL地址是没问题的,但是微信回调时,必须得是HTTP访问的80端口,这里比较坑,深深的鄙视腾讯一翻。


解析回调信息的代码如下:

/**
* @Author: HuangLongPu
* @Date: 2020/11/13 17:12
*/
@Api(tags = {"微信回调信息接口"})
@RestController
@RequestMapping("/message/weixin")
public class WeixinController {

private static final String token = "token"; // 可随意配置
private static final String encodingAesKey = "encodingAesKey"; // 可自动生成
private static final String appId = "appID";

@Reference
private IWeixinAccountInfoService weixinAccountInfoService;

@RequestMapping("callback")
public String callback(HttpServletRequest request) {
String echostr = null;
try {
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
String msgSignature = request.getParameter("msg_signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
echostr = request.getParameter("echostr");
// 第三方收到公众号平台发送的消息
String sourceXml = this.getBodyInfo(request);
String decrptXml = pc.decryptMsg(msgSignature, timestamp, nonce, sourceXml);
Map<String, String> map = xmlToMap(decrptXml);
String msgType = map.get(WeixinResponseField.FIELD_MSG_TYPE);
if(msgType == null) {
return echostr;
}
if(WeixinResponseField.MSG_TYPE_EVENT.equals(msgType)) {
String event = map.get(WeixinResponseField.FIELD_EVENT);
if(WeixinResponseField.EVENT_SUBSCRIBE.equals(event) || WeixinResponseField.EVENT_UNSUBSCRIBE.equals(event)) {
this.weixinAccountInfoService.update(map.get(WeixinResponseField.FIELD_FROM_USER_NAME), event);
}
}

} catch (AesException e) {
e.printStackTrace();
}

System.out.println("echostr >> " + echostr);
return echostr;
}

/**
* 获取POST请求中Body参数
* @param request
* @return 字符串
*/
public String getBodyInfo(HttpServletRequest request) {
BufferedReader br = null;
try {
ServletInputStream inputStream = request.getInputStream();
if(inputStream == null) {
return null;
}
br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}

if(br == null) {
return null;
}

String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}

return sb.toString();
}

/**
* XML格式字符串转换为Map
* @param xml XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String xml) {
try {
Map<String, String> data = new HashMap<>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
stream.close();
return data;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}


微信支持类下载地址:https://res.wx.qq.com/op_res/-serEQ6xSDVIjfoOHcX78T1JAYX-pM_fghzfiNYoD8uHVd3fOeC0PC_pvlg4-kmP

如果在进行回调验证时报错:java.lang.IllegalArgumentException: Last encoded character (before the paddings if any) 错误,参考解决文章:https://blog.csdn.net/qq_36830575/article/details/106646545