企业微信: https://work.weixin.qq.com/
概述
企业微信在推送消息给企业时,会对消息内容做AES加密,以XML格式POST到企业应用的URL上。
企业在被动响应时,也需要对数据加密,以XML格式返回给企业微信。
通知回调地址配置
获取access_token
请求方式: GET(HTTPS)
请求地址: https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
public final static String access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={corpsecret}";/*** 获取access_token** @param appid 凭证* @param appsecret 密钥* @return*/public static AccessToken getAccessToken(String appid, String appsecret) {AccessToken accessToken = null;String requestUrl = access_token_url.replace("{corpId}", appid).replace("{corpsecret}", appsecret);JSONObject jsonObject = httpRequest(requestUrl, "GET", null);// 如果请求成功if (null != jsonObject) {try {accessToken = new AccessToken();accessToken.setToken(jsonObject.getString("access_token"));accessToken.setExpiresIn(jsonObject.getInt("expires_in"));} catch (JSONException e) {accessToken = null;// 获取token失败log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}}return accessToken;}
获取客户群列表
请求方式:POST(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/list?access_token=ACCESS_TOKEN
/*** 获取客户群列表* @param accessToken* @param limit* @return*/public List<String> getGroupList(String accessToken, String limit){HashMap<String, Object> result = new HashMap<>();result.put("limit",limit);String jsonString = JSONObject.toJSONString(result);
// System.out.println("jsonString = " + jsonString);//1.获取调用地址groupList_url = groupList_url.replace("ACCESS_TOKEN",accessToken);//2.调用接口,查询客户列表net.sf.json.JSONObject jsonObject = WeiXinUtil.httpRequest(groupList_url, "POST", jsonString);if (null != jsonObject) {String group_chat_list = jsonObject.getString("group_chat_list");List<Chat> chatList = JSONObject.parseObject(group_chat_list, new TypeReference<List<Chat>>() {});//list用来存放客户群idArrayList<String> arrayList = new ArrayList<>();for (Chat chat : chatList) {arrayList.add(chat.getChatId());}//3.异常处理if (0 != jsonObject.getInt("errcode")) {log.error("获取客户群列表失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}return arrayList;}return null;}
获取客户群详情
/*** 获取客户群详情* @param accessToken* @param chatId* @param needName* @return*/public Map<String, Object> groupListDetails(String accessToken, String chatId, String needName){HashMap<String, Object> result = new HashMap<>();result.put("chat_id",chatId);result.put("need_name",needName);String jsonString = JSONObject.toJSONString(result);
// System.out.println("jsonString = " + jsonString);//1.获取调用地址groupListDetails_url = groupListDetails_url.replace("ACCESS_TOKEN",accessToken);//2.调用企业微信地址net.sf.json.JSONObject jsonObject = WeiXinUtil.httpRequest(groupListDetails_url, "POST", jsonString);System.out.println("jsonObject = " + jsonObject);if (null != jsonObject) {String group_chat = jsonObject.getString("group_chat");
// System.out.println("group_chat = " + group_chat);GroupChat groupChat = JSONObject.parseObject(group_chat, GroupChat.class);
// System.out.println("groupChat = " + groupChat);List<Member> memberList = groupChat.getMemberList();//获取到最近一次入群的用户memberList.sort(new Comparator<Member>() {@Overridepublic int compare(Member o1, Member o2) {return Long.compare(o2.getJoinTime(),o1.getJoinTime());}});//结果处理HashMap<String, Object> resultMap = new HashMap<>();resultMap.put("chatName",groupChat.getName());resultMap.put("member",memberList.get(0));//3.异常处理if (0 != jsonObject.getInt("errcode")) {log.error("获取客户群列表失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}return resultMap;}return null;}
加解密库下载地址 :加解密库下载与返回码 - 接口文档 - 企业微信开发者中心
- com\qq\weixin\mp\aes目录下是用户需要用到的接入企业微信的接口,其中WXBizMsgCrypt.java文件提供的WXBizMsgCrypt类封装了用户接入企业微信的三个接口,其它的类文件用户用于实现加解密,用户无须关心。sample.java文件提供了接口的使用示例。
- WXBizMsgCrypt封装了VerifyURL, DecryptMsg, EncryptMsg三个接口,分别用于开发者验证接收消息的url、接收消息的解密以及开发者回复消息的加密过程。使用方法可以参考Sample.java文件。
- 请开发者使用jdk1.6或以上的版本。针对org.apache.commons.codec.binary.Base64,需要导入jar包commons-codec-1.9(或comm ons-codec-1.8等其他版本),我们有提供,官方下载地址:下载
- 异常java.security.InvalidKeyException:illegal Key Size的解决方案:在官方网站下载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目录下覆盖原来文件。
示例: 对用户回复的消息解密
/*------------使用示例:对用户回复的消息解密---------------用户回复消息或者点击事件响应时,企业会收到回调消息,此消息是经过企业微信加密之后的密文以post形式发送给企业,密文格式请参考官方文档假设企业收到企业微信的回调消息如下:POST /cgi-bin/wxpush? msg_signature=477715d11cdb4164915debcba66cb864d751f3e6×tamp=1409659813&nonce=1372623149 HTTP/1.1Host: qy.weixin.qq.comContent-Length: 613<xml> <ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt><AgentID><![CDATA[218]]></AgentID></xml>企业收到post请求之后应该 1.解析出url上的参数,包括消息体签名(msg_signature),时间戳(timestamp)以及随机数字串(nonce)2.验证消息体签名的正确性。3.将post请求的数据进行xml解析,并将<Encrypt>标签的内容进行解密,解密出来的明文即是用户回复消息的明文,明文格式请参考官方文档第2,3步可以用企业微信提供的库函数DecryptMsg来实现。
*/
public static void main(String[] args) {// String sReqMsgSig = HttpUtils.ParseUrl("msg_signature");String sReqMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6";// String sReqTimeStamp = HttpUtils.ParseUrl("timestamp");String sReqTimeStamp = "1409659813";// String sReqNonce = HttpUtils.ParseUrl("nonce");String sReqNonce = "1372623149";// post请求的密文数据// sReqData = HttpUtils.PostData();String sReqData = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt><AgentID><![CDATA[218]]></AgentID></xml>";try {String sMsg = wxcpt.DecryptMsg(sReqMsgSig, sReqTimeStamp, sReqNonce, sReqData);System.out.println("after decrypt msg: " + sMsg);// TODO: 解析出明文xml标签的内容进行处理// For example:DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(sMsg);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Content");String Content = nodelist1.item(0).getTextContent();System.out.println("Content:" + Content);} catch (Exception e) {// TODO// 解密失败,失败原因请查看异常e.printStackTrace();}
}