一:企微实现和企业间的微信客服消息接收和事件原理
新版企微主要通过2个阶段实,第一个:消息推送 概述 - 文档 - 企业微信开发者中心 ,第二个:微信客服 接收消息和事件 - 文档 - 企业微信开发者中心
二:代码逻辑分析
1、消息推送 概述 - 文档 - 企业微信开发者中心
企微所有的消息都是通过这个主体接收,然后将消息内容放在Encrypt节点里面,如下图
返回对象log打印结果实例:
后端日志控制台返回:
<xml><ToUserName><![CDATA[wwaf9f23ec4871e]]></ToUserName><Encrypt><![CDATA[LJzYB4kTu5qFbSDoW1beSl7ZlMBMsf8rvFPO7aaO/UNUipytMzGer6oQBxGcdvtd4zE6o7OPliGvUqxdOaiTIUeUAbqvSld92guwwUXhlsaipIfPo7BU2SJ2Lg5xClCXmTSRA2PZ6NVEk6n9WLjHieXzNp/1pLVjadNueXVZjQLCEL2yQQmZmSMqGlz4KfKy9XfkyuyKCTEmBIcwmOxMEvWtslQOtlSDnZhCV6KNkV4gK+dlBa0gJQthpTYt7UGyB5PagFQhDDdpFRonyE746BiNkNNyqxyCQBCp50ldWItuixEUM40LL95V0Z/wKaQDkPMV1H1WRrr+9eqUM3/NAz0HojMtTjrqIlDimwiEkMgOlMY6PLwKP9JdDrjJU3VfDS5SxYOpnQxmBU6JLk7yAsd1ssb1LaHwReCciW4vVUoTKHhQ5BTkRpHfHQ7TuTQ1bhPZKE5yXK7hkXKG8XSxnbCiMmnp+Szm41K4XpOr8QOmmKsrX646hGyja489IrKMaZ3LzCT2vHKloBozsurvydrqkUhVoU5my/r7yE3ctMLxua7mvXX3AbzIXt7lt4OfhNIyo1Am5oGAA==]]></Encrypt><AgentID><![CDATA[1060109]]></AgentID></xml>
客服消息也会在Encrypt里面
2、微信客服 接收消息和事件 - 文档 - 企业微信开发者中心
获取客服消息,需要解析主体中的Encrypt,拿到里面的数据。
解密后,你会发现,Encrypt里面的内容就是如下格式数据:
<xml><ToUserName><![CDATA[ww12345678910]]></ToUserName><CreateTime>1348831860</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[kf_msg_or_event]]></Event><Token><![CDATA[ENCApHxnGDNAVNY4AaSJKj4Tb5mwsEMzxhFmHVGcra996NR]]></Token><OpenKfId><![CDATA[wkxxxxxxx]]></OpenKfId>
</xml>
到此,逻辑就基本出来了!
很重要的一点要理解,这个消息的使用逻辑,看企微api文档你发现就是这一块那一块的。实际就是做的一件事,实际就是一个包含关系—— 微【信客服-接收消息和事件】都是放在 【企业微信-消息推送】这个大的主体里面的!
企微后台自建应用绑定事件url:
理解到这里,基本就很简单了。
下面是一个java Servlet的实现!
package org.jeecg.modules.ww.kf.servlet;import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.sys.qiwei.service.QiWeiEventStrategy;
import org.jeecg.modules.ww.kf.util.UtilHelp;
import org.jeecg.modules.ww.sys.json_callback.com.qq.weixin.mp.aes.AesException;
import org.jeecg.modules.ww.sys.json_callback.com.qq.weixin.mp.aes.WXBizJsonMsgCrypt;
import org.jeecg.modules.ww.wechatkfmsgrecord.service.ITblWechatKfMsgRecordService;
import org.jeewx.api.mp.aes.WXBizMsgCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;@Slf4j
@WebServlet(name = "kfCallbackServlet",urlPatterns = "/qw/kf-callback")
public class WeiXinKfCallbackServlet extends HttpServlet {@Autowiredprivate ITblWechatKfMsgRecordService kfMsgRecordService;@Autowiredprivate Map<String, QiWeiEventStrategy> qiWeiEventStrategyMap;private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public WeiXinKfCallbackServlet() {super();}@Overridepublic void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 处理Servlet请求逻辑log.info("WeiXinCheckUrl 处理Servlet请求逻辑");String method = request.getMethod();if ("GET".equalsIgnoreCase(method)) {// 处理GET请求doGet(request,response);} else if ("POST".equalsIgnoreCase(method)) {// 处理POST请求doPost(request,response);}}/*** get方法主要用来做验证企微事件的url* msg_signature 是 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体* timestamp 是 时间戳* nonce 是 随机数* echostr 是 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、CorpID四个字段,其中msg即为消息内容明文*/@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 获取企业参数String sVerifyMsg_signature = request.getParameter("msg_signature");String sVerifyTimestamp = request.getParameter("timestamp");String sVerifyNonce = request.getParameter("nonce");String sVerifyEchostr = request.getParameter("echostr");log.info("WeiXinCheckUrl doGet params msg_signature:{},timestamp:{},nonce:{},echostr:{}",sVerifyMsg_signature,sVerifyTimestamp,sVerifyNonce,sVerifyEchostr);String sToken= "Hid7AO1DdL3FRQMJeonqURipY9";String sEncodingAESKey = "H6MitIu236jCO4uS8j2nVH4iGzkKf1oKGnbeHGnOb5o";String sCorpID = "wwaf9f523ec40871ee";WXBizJsonMsgCrypt wxcpt = null;try {wxcpt = new WXBizJsonMsgCrypt(sToken, sEncodingAESKey, sCorpID);} catch (AesException e) {e.printStackTrace();}String sEchoStr = null; //需要返回的明文try {sEchoStr = wxcpt.VerifyURL(sVerifyMsg_signature, sVerifyTimestamp,sVerifyNonce, sVerifyEchostr);log.info("WeiXinCheckUrl doGet verifyurl echostr:{}",sEchoStr);// 验证URL成功,将sEchoStr返回 验证url这里可以不用管// HttpUtils.SetResponse(sEchoStr);} catch (Exception e) {//验证URL失败,错误原因请查看异常e.printStackTrace();}UtilHelp.PrintWriter(sEchoStr, "json", request, response);}/*** doPost是用来接收企微发送post数据,如微信端给企微发消息后,企微把消息推送给我们后端服务的时候!* msgSignature 签名串,对应URL参数的msg_signature* timeStamp 时间戳,对应URL参数的timestamp* nonce 随机串,对应URL参数的nonce* postData 密文,对应POST请求的数据*/@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String result = "";String msgSignature = request.getParameter("msg_signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));String lines;StringBuffer bodyXml = new StringBuffer("");while ((lines = reader.readLine()) != null) {lines = new String(lines.getBytes(), "utf-8");bodyXml.append(lines);}log.info("qwKfEvent params timestamp:{},nonce:{},msg_signature:{},data:{}",timestamp,nonce,msgSignature,bodyXml);try {WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("Hid7AODdL3FRQMJeqURip9","H6MitIu236CO4uS8j2H4iGzkKfKGnbHnOb5o","wwa9f523ec4081e");result = wxcpt.decryptMsg(msgSignature,timestamp,nonce,String.valueOf(bodyXml));} catch (org.jeewx.api.mp.aes.AesException e) {log.info("qwKfEvent 解密失败 Exeception:{}",e.getMessage());}cn.hutool.json.JSONObject eventObj = JSONUtil.xmlToJson(result);log.info("[qwKfEvent] 解密后内容={}", eventObj.toString());// 这里解密后就是主体里面的微信消息了,可以参考微信消息事件的响应xml内容去做对应的解析String event = eventObj.getByPath("xml.Event", String.class);// 获取事件处理实现类 (这里处理你自己的业务,或者搞个实现类去实现如下,就不贴代码了)QiWeiEventStrategy eventStrategy = qiWeiEventStrategyMap.get(String.format("%s_service", event));if (eventStrategy != null) {eventStrategy.handleEvent(eventObj);log.info("[qwKfEvent] 事件处理完成. eventName={}", event);} else {log.info("[qwKfEvent] 未找到合适对应的事件实现类. eventName={}", event);}}}
里面使用到的企微api:weworkapi_java-master.zip 去csdn下载即可,也可以使用jar包
servlet的话,自己创建个配置注入到spring资源管理就行:
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ServletConfig {@Beanpublic ServletRegistrationBean<WeiXinKfCallbackServlet> myServletRegistration() {ServletRegistrationBean<WeiXinKfCallbackServlet> registration = new ServletRegistrationBean<>(new WeiXinKfCallbackServlet(), "/qw/kf-callback");return registration;}}
本人QQ: 93775988