关于接收消息

为了能够让自建应用和企业微信进行双向通信,企业可以在应用的管理后台开启接收消息模式。
开启接收消息模式的企业,需要提供可用的接收消息服务器URL。
开启接收消息模式后,用户在应用里发送的消息会推送给企业后台。此外,还可配置地理位置上报等事件消息,当事件触发时企业微信会把相应的数据推送到企业的后台。
企业后台接收到消息后,可在回复该消息请求的响应包里带上新消息,企业微信会将该被动回复消息推送给用户。

开启接收消息

设置接收消息的参数

在企业的管理端后台,进入需要设置接收消息的目标应用,点击“接收消息”的“设置API接收”按钮,进入配置页面。

要求填写应用的URL、Token、EncodingAESKey三个参数

  • URL是企业后台接收企业微信推送请求的访问协议和地址,支持http或https协议。
  • Token可由企业任意填写,用于生成签名。
  • EncodingAESKey用于消息体的加密,是AES密钥的Base64编码。

这三个参数的用处在加解密方案说明章节会介绍,此处不用细究。

验证URL有效性

当点击“保存”提交以上信息时,企业微信会发送一条验证消息到填写的URL,发送方法为GET
企业的接收消息服务器接收到验证请求后,需要作出正确的响应才能通过URL验证。

企业在获取请求时需要做Urldecode处理,否则会验证不成功
你可以访问接口调试工具进行调试,依次选择 建立连接 > 测试回调模式。

假设接收消息地址设置为:http://api.3dept.com/,企业微信将向该地址发送如下验证请求:

请求方式:GET
请求地址:http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR
参数说明

参数 必须 说明
msg_signature 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体
timestamp 时间戳
nonce 随机数
echostr 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、CorpID四个字段,其中msg即为消息内容明文

企业后台收到请求后,需要做如下操作:

  1. 对收到的请求做Urldecode处理
  2. 通过参数msg_signature对请求进行校验,确认调用者的合法性。
  3. 解密echostr参数得到消息内容(即msg字段)
  4. 在1秒内原样返回明文消息内容(不能加引号,不能带bom头,不能带换行符)

以上2~3步骤可以直接使用验证URL函数一步到位。
之后接入验证生效,接收消息开启成功。

使用接收消息

开启接收消息模式后,企业微信会将消息发送给企业填写的URL,企业后台需要做正确的响应。

接收消息协议的说明

  • 企业微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。如果企业在调试中,发现成员无法收到被动回复的消息,可以检查是否消息处理超时。
  • 当接收成功后,http头部返回200表示接收ok,其他错误码企业微信后台会一律当做失败并发起重试。
  • 关于重试的消息排重,有msgid的消息推荐使用msgid排重。事件类型消息推荐使用FromUserName + CreateTime排重。
  • 假如企业无法保证在五秒内处理并回复,或者不想回复任何内容,可以直接返回200(即以空串为返回包)。企业后续可以使用主动发消息接口进行异步回复。

接收消息请求的说明

假设企业的接收消息的URL设置为http://api.3dept.com。
请求方式:POST
请求地址 :http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323

接收数据格式 :

  1. <xml>
  2. <ToUserName><![CDATA[toUser]]></ToUserName>
  3. <AgentID><![CDATA[toAgentID]]></AgentID>
  4. <Encrypt><![CDATA[msg_encrypt]]></Encrypt>
  5. </xml>

参数说明

参数 说明
ToUserName 企业微信的CorpID,当为第三方套件回调事件时,CorpID的内容为suiteid
AgentID 接收的应用id,可在应用的设置页面获取
Encrypt 消息结构体加密后的字符串

企业收到消息后,需要作如下处理:

  1. 对msg_signature进行校验
  2. 解密Encrypt,得到明文的消息结构体(消息结构体后面章节会详说)
  3. 如果需要被动回复消息,构造被动响应包
  4. 正确响应本次请求

以上1~2步骤可以直接使用解密函数一步到位。
3步骤其实包含加密被动回复消息、生成新签名、构造被动响应包三个步骤,可以直接使用加密函数一步到位。

被动响应包的数据格式:

  1. <xml>
  2. <Encrypt><![CDATA[msg_encrypt]]></Encrypt>
  3. <MsgSignature><![CDATA[msg_signature]]></MsgSignature>
  4. <TimeStamp>timestamp</TimeStamp>
  5. <Nonce><![CDATA[nonce]]></Nonce>
  6. </xml>

参数说明

参数 是否必须 说明
Encrypt 经过加密的消息结构体
MsgSignature 消息签名
TimeStamp 时间戳
Nonce 随机数,由企业自行生成

 java解密:

json版本(2020年9月1日更新,点击下载

注意事项:

  • 1.企业微信回调sdk json版本
  • 2.com\qq\weixin\mp\aes目录下是用户需要用到的接入企业微信的接口,其中WXBizJsonMsgCrypt.java文件提供的WXBizJsonMsgCrypt类封装了用户接入企业微信的三个接口,其它的类文件用户用于实现加解密,用户无须关心。sample.java文件提供了接口的使用求例。
  • 3.WXBizJsonMsgCrypt封装了VerifyURL, DecryptMsg, EncryptMsg三个接口,分别用于开发者验证回调url,收到用户回复消息的解密以及开发者回复消息的加密过程。使用方法可以参考Sample.java文件。
  • 4.加解密协议请参考企业微信官方文档。
  • 5.请开发者使用jdk1.6以上的版本。针对org.apache.commons.codec.binary.Base64,需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本),我们有提供,官方下载地址:Codec – Download Apache Commons Codec
  • 6.针对json解析,需要编译导入架包json.jar,我们有提供,官方源码下载地址 : GitHub - stleary/JSON-java: A reference implementation of a JSON package in Java.

  • 7.异常java.security.InvalidKeyException:illegal Key Size的解决方案:
    在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 Download
    下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt。如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件,如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件

  • 此处参考博客:java.security.InvalidKeyException: Illegal key size错误_伫望向北的博客-CSDN博客

  • 记得重启项目哦

更多参考官方文档:接收消息与事件 - 企业微信API

以下我自己用Servlet写的对接代码

package com.thinkgem.jeesite.util;import com.google.common.collect.Maps;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizJsonMsgCrypt;
import com.thinkgem.jeesite.common.mapper.JsonMapper;
import com.thinkgem.jeesite.common.utils.StringUtils;
import com.thinkgem.jeesite.common.web.Servlets;
import com.thinkgem.jeesite.modules.sys.entity.Ctiserver;
import com.thinkgem.jeesite.modules.sys.entity.InterfaceLog;
import com.thinkgem.jeesite.modules.sys.entity.Queue;
import com.thinkgem.jeesite.modules.sys.entity.User;
import com.thinkgem.jeesite.modules.sys.service.CtiserverService;
import com.thinkgem.jeesite.modules.sys.service.InterfaceLogService;
import com.thinkgem.jeesite.modules.sys.service.QueueService;
import com.thinkgem.jeesite.modules.sys.service.SystemService;
import com.thinkgem.jeesite.modules.sys.utils.SysUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestParam;import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class weiXinCheckUrl extends HttpServlet{@Autowiredprivate InterfaceLogService interfaceLogService;private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public weiXinCheckUrl() {super();}/*** 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 msg_signature = request.getParameter("msg_signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");System.out.println("消息推送前验证=============================================");System.out.println("msg_signature:"+msg_signature);System.out.println("timestamp:"+timestamp);System.out.println("nonce:"+nonce);System.out.println("echostr:"+echostr);String sToken= SysUtils.getSysConfigValue("check_url_token", "");String sEncodingAESKey = SysUtils.getSysConfigValue("check_url_key", "");String sCorpID = SysUtils.getSysConfigValue("enterprise_wechat_id", "");WXBizJsonMsgCrypt wxcpt = null;try {wxcpt = new WXBizJsonMsgCrypt(sToken, sEncodingAESKey, sCorpID);} catch (AesException e) {e.printStackTrace();}String sEchoStr = ""; //需要返回的明文try {sEchoStr = wxcpt.VerifyURL(msg_signature, timestamp,nonce, echostr);System.out.println("返回的消息是===verifyurl echostr: " + sEchoStr);// 验证URL成功,将sEchoStr返回// HttpUtils.SetResponse(sEchoStr);} catch (Exception e) {//验证URL失败,错误原因请查看异常e.printStackTrace();}UtilHelp.PrintWriter(sEchoStr, "json", request, response);}/***  msgSignature 签名串,对应URL参数的msg_signature* timeStamp 时间戳,对应URL参数的timestamp* nonce 随机串,对应URL参数的nonce* postData 密文,对应POST请求的数据*/@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Date beginCreateDate=new Date();String isSuccess="0";String returnValueNormal="0";String results="";String requestVal="";String sReqMsgSig = request.getParameter("msg_signature");String sReqTimeStamp = request.getParameter("timestamp");String sReqNonce = request.getParameter("nonce");BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));requestVal="msg_signature:"+sReqMsgSig+"; timestamp:"+sReqTimeStamp+";nonce "+sReqNonce;String lines;StringBuffer bodyXml = new StringBuffer("");while ((lines = reader.readLine()) != null) {lines = new String(lines.getBytes(), "utf-8");bodyXml.append(lines);}System.out.println(bodyXml);Document Doc = null;String strXml="";if(StringUtils.isNotBlank(bodyXml)){strXml=bodyXml.toString().replaceAll("<!\\[CDATA\\[","").replaceAll("]]>","");try {Doc = DocumentHelper.parseText(UtilHelp.formatXml(strXml)); // 将字符串转为XML} catch (DocumentException e) {returnValueNormal="1";results="解析转为xml";e.printStackTrace();}}String toUserName = "";String AgentID = "";String Encrypt = "";Element root = Doc.getRootElement(); // 获取根节点List<Element>  contents = root.content();for(int i=0;i<contents.size();i++){Element element = contents.get(i);String text = element.getText();if("ToUserName".equals(element.getName())){toUserName=element.getText();System.out.println("用户"+toUserName+" ;");}else  if("AgentID".equals(element.getName())){AgentID=element.getText();System.out.println("在应用id为:"+AgentID+"的因应用 ;");}else  if("Encrypt".equals(element.getName())){Encrypt=element.getText();System.out.println("发送了消息为:"+Encrypt+" ;");}System.out.println(element.getText());}System.out.println("消息推送前验证=============================================");System.out.println("sReqMsgSig:"+sReqMsgSig);System.out.println("sReqTimeStamp:"+sReqTimeStamp);System.out.println("sReqNonce:"+sReqNonce);System.out.println("body数据是哪个?:");System.out.println("strXml:"+strXml);String sToken= SysUtils.getSysConfigValue("check_url_token", "");String sEncodingAESKey = SysUtils.getSysConfigValue("check_url_key", "");String sCorpID = SysUtils.getSysConfigValue("enterprise_wechat_id", "");WXBizJsonMsgCrypt wxcpt = null;try {wxcpt = new WXBizJsonMsgCrypt(sToken, sEncodingAESKey, sCorpID);} catch (AesException e) {e.printStackTrace();}String sMsg= ""; //需要返回的明文try {sMsg = wxcpt.DecryptMsg(sReqMsgSig, sReqTimeStamp, sReqNonce, Encrypt);System.out.println("解密后得到的消息是===sMsg: " + sMsg);} catch (Exception e) {e.printStackTrace();returnValueNormal+="解密消息失败";}String CreateTime=new Date().getTime()+"";//返回消息格式/*** Encrypt  是   经过加密的消息结构体MsgSignature  是   消息签名TimeStamp   是   时间戳Nonce    是   随机数,由企业自行生成**/String sEncryptMsg="";/* String sRespData = "{\"ToUserName\":\"wx5823bf96d3bd56c7\"," +"\"FromUserName\":\"mycreate\"," +"\"CreateTime\": 1409659813," +"\"MsgType\":\"text\"," +"\"Content\":\"hello\"," +"\"MsgId\":4561255354251345929," +"\"AgentID\": 218}";*/String Content="我回复了你的消息,抱歉,暂时我只会回复这一句话!";Map<String,String> resultMap=new HashMap<>();resultMap.put("ToUserName",toUserName);resultMap.put("CreateTime",CreateTime);resultMap.put("MsgType","text");resultMap.put("Content",Content);resultMap.put("MsgId",sReqMsgSig);resultMap.put("AgentID",sCorpID);String jsonString = JsonMapper.toJsonString(resultMap);try{sEncryptMsg = wxcpt.EncryptMsg(jsonString, sReqTimeStamp, sReqNonce);System.out.println("after encrypt sEncrytMsg: " + sEncryptMsg);// 加密成功// TODO:// HttpUtils.SetResponse(sEncryptMsg);}catch(Exception e) {e.printStackTrace();// 加密失败}String returnXml="<xml>\n" +"   <Encrypt><![CDATA["+sEncryptMsg+"]]></Encrypt>\n" +"   <MsgSignature><![CDATA["+sReqMsgSig+"]]></MsgSignature>\n" +"   <TimeStamp>"+CreateTime+"</TimeStamp>\n" +"   <Nonce><![CDATA["+sReqNonce+"]]></Nonce>\n" +"</xml>";requestVal="msg_signature:"+sReqMsgSig+"; timestamp:"+sReqTimeStamp+";nonce "+sReqNonce+"; 加密的xml"+strXml+"解密的消息:"+sMsg;InterfaceLog interfaceLog = new InterfaceLog("5",requestVal,returnValueNormal,results,isSuccess,beginCreateDate,new Date());interfaceLogService.save(interfaceLog);UtilHelp.PrintWriter(returnXml, "Xml", request, response);}@Testpublic void test(){String strXml="<xml><ToUserName><![CDATA[ww427c0be81be8555f]]></ToUserName><Encrypt><![CDATA[gICWNYyM3DUknCuhhNy9HVjvheQ+qcJsBl+k0Qzym8xruXYxWom7Dg4GnFAOdiTejmseEwMlXX5M9Nh/Cg3MhzC7T/KCN7hhT65igaHhxJQgmavkaC7RbrzBIH04EN2dYd+IbB5VoM7IRTeYbkO1nmpYIMIVQehCfSbqsxqikjHybvO8hhUfyLxLCWq5VtON2XMHNDn5fndizdKPCzBIEvEhjsiD8yRVIufldgc6vYz8CkfUQdeo9bSwm2VYa7hk6EO8/OEgVvHOA1w+7WrKQhnw32fAwQKhQTjQzNhS+gXhbCgmIbZMt2f5rck3Zl/8POwvdDMiZR3hnCO0IO3uOvbKpZOkeTSHVvB+uxOsSZuCQkkyYKGLetGKECEdp6J8MtAdALZQVZjyyeI0sjH1PdjEbv7AaMOrtaeJMykOsA8=]]></Encrypt><AgentID><![CDATA[]]></AgentID></xml>";System.out.println(strXml);Document Doc = null;if(StringUtils.isNotBlank(strXml)){strXml=strXml.replaceAll("<!\\[CDATA\\[","").replaceAll("]]>","");try {Doc = DocumentHelper.parseText(UtilHelp.formatXml(strXml)); // 将字符串转为XML} catch (DocumentException e) {e.printStackTrace();}}Element root = Doc.getRootElement(); // 获取根节点List<Element>  contents = root.content();for(int i=0;i<contents.size();i++){Element element = contents.get(i);String text = element.getText();System.out.println(element.getName()+":");System.out.println(text);}}@Overridepublic void init(ServletConfig config) throws ServletException {System.out.println("~~~微信消息推送及验证~~~");}public static byte[] readAsBytes(HttpServletRequest request) {int len = request.getContentLength();byte[] buffer = new byte[len];ServletInputStream in = null;try{in = request.getInputStream();in.read(buffer, 0, len);in.close();}catch (IOException e){e.printStackTrace();}finally{if (null != in){try{in.close();}catch (IOException e){e.printStackTrace();}}}return buffer;}}

对接企业微信4:接收消息与事件相关推荐

  1. Nodejs版本的企业微信中接收消息与腾讯对接之验证URL 代码已经上传,可以去下载

    1 当我们开发企业微信中的消息推送的时候,如果要接收来自用户的信息,需要提供一个接收消息的服务器地址来对接,如果使用nodejs来开发的话,按照文档说明,你可能不会成功.下面总结下我自己的步骤,已经成 ...

  2. Nodejs版本的企业微信中接收消息与腾讯对接之接收消息 代码已经上传,可以去下载

    1 再经过前面的URL验证成功后,我们下面可以来写接收消息的请求了. 2 官方是这样说明的, msg_signature,timestamp,nonce参数的值还是比较好获取,接收的数据: <x ...

  3. java对接企业微信机器人发送消息

    [Java]企业微信机器人自动发送群消息 先在群里添加机器人,然后获取机器人的webhook地址: 假设webhook是:https://qyapi.weixin.qq.com/cgi-bin/web ...

  4. 企业微信设置接收消息的参数,报错“openapi回调地址请求不通过”

    接口文档地址:https://developer.work.weixin.qq.com/document/10514 在保存时提示:openapi回调地址请求不通过. 验证URL有效性这步有问题,查看 ...

  5. 企业微信 消息 html,企业微信怎么设置消息提醒

    企业微信是一款非常不错的办公软件,用户加入企业群就能实时了解企业的动态.而且大家只需设置消息提醒,软件就会在第一时间通知你,不会让你错过任何重要的消息,下面小编为大家带来相关的设置教程. 方法/步骤分 ...

  6. Java集成企业微信中的微信客服[接收消息和事件]

    目录 第一步登录企业微信后台管理界面 第二步设置接收事件服务器 第三步  先下载企业微信提供的解密工具包 PKCS7Encoder  对企业微信发送给企业后台的消息加解密示例代码 XMLParse  ...

  7. 企业微信 接收消息服务器,接收消息与事件

    [TOC] 关于接收消息 为了能够让自建应用和企业微信进行双向通信,企业可以在应用的管理后台开启接收消息模式. 开启接收消息模式的企业,需要提供可用的接收消息服务器URL. 开启接收消息模式后,用户在 ...

  8. 企业微信接收信息服务器全代码,微信企业号的消息和事件的接收处理及解密

    在上篇随笔介绍了有关企业号的消息发送,官方特别声明消息是不用加密发送的.但是在回调的服务器上,也就是我们网站的服务器上,微信传过来的消息是加密的,需要我们调用类库对消息和事件进行解密操作,由于官方的例 ...

  9. C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

    在上篇随笔<C#开发微信门户及应用(19)-微信企业号的消息发送(文本.图片.文件.语音.视频.图文消息等)>介绍了有关企业号的消息发送,官方特别声明消息是不用加密发送的.但是在回调的服务 ...

最新文章

  1. 美团的android多渠道包的3种方法
  2. 虚拟化四路服务器,专为虚拟化设计 戴尔R905四路服务器评测
  3. 知识图谱学习笔记-PageRank
  4. leetcode 235. 二叉搜索树的最近公共祖先
  5. 聊聊WebRTC网关服务器2:如何选择PeerConnection方案?
  6. 代码自解释不是不写注释的理由
  7. 创新 - 王屋村的魔方们
  8. SpringMVC 执行流程解析
  9. 浅入浅出 Android 安全:第二章 Android Linux 内核层安全
  10. AS3的Vector的初始化
  11. Net-Snmp安装配置
  12. 实战HTML:静态登录界面
  13. 图像基础ImageMagick图像处理
  14. Matlab中的plotyy用法总结
  15. Angular学习笔记第三章——创建组件
  16. 精品丨DAX性能优化问题
  17. 从字符串中提取IP子串(C语言)
  18. 反思专家型领导---转载--读后感
  19. Dubbo相关问题如何用管程实现异步转同步?
  20. office972003_Microsoft Office Word 97-2003 文档

热门文章

  1. Codeup——597 | 问题 A: Set Similarity (25)
  2. buuctf_crypto
  3. 如何正确的make menuconfig
  4. Idea pull时 conflicts
  5. 英语语言学c刊目录,外语类CSSCI核心期刊(2017-2018)
  6. 网络打印服务器 linux,在Linux环境下搭建网络打印服务器
  7. Win764位系统下使用ObRegisterCallbacks内核函数来实现进程保护
  8. HTTPS、SSL、TLS三者之间的联系和区别
  9. 火山PC_QQ登录源码(QQ互联)
  10. Springboot 整合ElasticSearch(Client篇)