我们在开发企业内部应用时,需要实现内部应用和企业微信的双向同步,即互联互通。
举个例子
同步一:企业内部OA系统在修改内部通讯录时,可以同步企业微信(直接调API接口即可)
同步二:在企业微信后台修改通讯录时,反馈给内部OA系统(本文讲解)
总的来说,实现通讯录回调的流程分为四步:
第一步:管理员在企业微信在后台修改通讯录信息(除了API接口修改之外的都算)
第二步:企业微信修改通讯录后,以XML的方式,向企业内部系统发送修改详情
第三步:企业内部系统收到XML信息后,解密信息
第四步:对于解密后的信息,修改企业内部系统的通讯录
具体实现
一、导包
下载企业微信提供的解密工具,放到自己的项目内。
注意com包不能改名字
如果1.9版本的包无法通过编译,可以使用1.4的包
1. <dependency> 2. <groupId>commons-codec</groupId> 3. <artifactId>commons-codec</artifactId> 4. <version>1.4</version> 5. </dependency>
二、验证URL
在企业微信管理后台,配置URL
test方法为验证URL的模板,拿来即用
ParameterSettings是我放固定字段的类,相应参数替换即可
1. import com.qq.weixin.mp.aes.AesException; 2. import com.qq.weixin.mp.aes.WXBizMsgCrypt; 3. 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.PrintWriter; 7. 8. /** 9. * 验证URL 10. * @param request 11. * @param response 12. * @throws Exception 13. */ 14. public void test(HttpServletRequest request, HttpServletResponse response) throws Exception { 15. // 微信加密签名 16. String msg_signature = request.getParameter("msg_signature"); 17. // 时间戳 18. String timestamp = request.getParameter("timestamp"); 19. // 随机数 20. String nonce = request.getParameter("nonce"); 21. // 随机字符串 22. String echostr = request.getParameter("echostr"); 23. 24. System.out.println("request=" + request.getRequestURL()); 25. 26. PrintWriter out = response.getWriter(); 27. // 通过检验msg_signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 28. String result = null; 29. try { 30. WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(ParameterSettings.YHHD_TOKEN, ParameterSettings.YHHD_EAK, ParameterSettings.AS_CORPID); 31. result = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr); 32. } catch (AesException e) { 33. e.printStackTrace(); 34. } 35. if (result == null) { 36. result = ParameterSettings.YHHD_TOKEN; 37. } 38. out.print(result); 39. out.close(); 40. out = null; 41. }
原文CSDN链接:https://zwz99.blog.csdn.net/article/details/113818974
三、解密
验证URL完成后,将该URL的接口方法替代成下方callBack方法的代码
当企业微信发送回调通知时,该方法会实现接收
该模板会将XML格式的数据转换为标准JSON,方便后续处理
JSON用了阿里的fastjson,maven依赖如下:
1. <dependency> 2. <groupId>com.alibaba</groupId> 3. <artifactId>fastjson</artifactId> 4. <version>1.2.9</version> 5. </dependency>
1. import com.alibaba.fastjson.JSONObject; 2. import com.qq.weixin.mp.aes.AesException; 3. import com.qq.weixin.mp.aes.WXBizMsgCrypt; 4. import org.springframework.web.bind.annotation.RequestMapping; 5. import org.springframework.web.bind.annotation.RequestMethod; 6. 7. import javax.servlet.ServletInputStream; 8. import javax.servlet.http.HttpServletRequest; 9. import javax.servlet.http.HttpServletResponse; 10. import java.io.BufferedReader; 11. import java.io.IOException; 12. import java.io.InputStreamReader; 13. import java.io.PrintWriter; 14. 15. 16. /** 17. * 企业微信发送XML到这个方法 18. * @param request 19. * @param response 20. * @throws Exception 21. */ 22. @RequestMapping(value = "/callBack", method = RequestMethod.POST) 23. public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception { 24. request.setCharacterEncoding("UTF-8"); 25. response.setCharacterEncoding("UTF-8"); 26. String respMessage = ""; 27. respMessage=getDecryptMsg(request); 28. //进行回调处理 29. dealCallBackEvent(respMessage); 30. PrintWriter out = response.getWriter(); 31. out.print(respMessage); 32. out.close(); 33. } 34. 35. /** 36. * 解密XML数据转换JSON 37. * @param request 38. * @return 39. */ 40. public String getDecryptMsg(HttpServletRequest request) { 41. String postData=""; // 密文,对应POST请求的数据 42. String result=""; // 明文,解密之后的结果 43. String msg_signature = request.getParameter("msg_signature"); // 微信加密签名 44. String timestamp = request.getParameter("timestamp"); // 时间戳 45. String nonce = request.getParameter("nonce"); // 随机数 46. try { 47. //1.获取加密的请求消息:使用输入流获得加密请求消息postData 48. ServletInputStream in = request.getInputStream(); 49. BufferedReader reader =new BufferedReader(new InputStreamReader(in)); 50. 51. String tempStr=""; //作为输出字符串的临时串,用于判断是否读取完毕 52. while(null!=(tempStr=reader.readLine())){ 53. postData+=tempStr; 54. } 55. //2.获取消息明文:对加密的请求消息进行解密获得明文 56. WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(ParameterSettings.YHHD_TOKEN, ParameterSettings.YHHD_EAK, ParameterSettings.YH_CORPID); 57. result = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postData); 58. } catch (IOException e) { 59. e.printStackTrace(); 60. } catch (AesException e) { 61. e.printStackTrace(); 62. } 63. return result; 64. } 65. 66. /** 67. * 对解密后的数据进行业务逻辑处理 68. * @param text 69. */ 70. private void dealCallBackEvent(String text) { 71. JSONObject json = XmlTool.documentToJSONObject(text); 72. String event = json.getString("Event"); 73. String changeType = json.getString("ChangeType"); 74. if (event.equals("change_contact")) { 75. if (changeType.equals("create_user")) { // 创建用户回调 76. String name = json.getString("Name"); 77. String userID = json.getString("UserID"); 78. String departmentStr = json.getString("Department"); // 部门为逗号分开的字符串 79. String[] departmentList = departmentStr.split(","); 80. String corpID = json.getString("ToUserName"); 81. String mobile = json.getString("Mobile"); 82. if (corpID.equals(ParameterSettings.YH_CORPID)) { // 替换企业ID 83. // 业务逻辑代码 84. } 85. } 86. } 87. }
核心代码到此结束,以下是其他所有代码
1. import java.util.List; 2. 3. import org.dom4j.Attribute; 4. import org.dom4j.Document; 5. import org.dom4j.DocumentException; 6. import org.dom4j.DocumentHelper; 7. import org.dom4j.Element; 8. 9. import com.alibaba.fastjson.JSONArray; 10. import com.alibaba.fastjson.JSONObject; 11. 12. public class XmlTool { 13. /** 14. * String 转 org.dom4j.Document 15. * @param xml 16. * @return 17. * @throws DocumentException 18. */ 19. public static Document strToDocument(String xml){ 20. try { 21. //加上xml标签是为了获取最外层的标签,如果不需要可以去掉 22. return DocumentHelper.parseText(xml); 23. } catch (DocumentException e) { 24. return null; 25. } 26. } 27. 28. /** 29. * org.dom4j.Document 转 com.alibaba.fastjson.JSONObject 30. * @param xml 31. * @return 32. * @throws DocumentException 33. */ 34. public static JSONObject documentToJSONObject(String xml){ 35. return elementToJSONObject(strToDocument(xml).getRootElement()); 36. } 37. 38. /** 39. * org.dom4j.Element 转 com.alibaba.fastjson.JSONObject 40. * @param node 41. * @return 42. */ 43. public static JSONObject elementToJSONObject(Element node) { 44. JSONObject result = new JSONObject(); 45. // 当前节点的名称、文本内容和属性 46. List<Attribute> listAttr = node.attributes();// 当前节点的所有属性的list 47. for (Attribute attr : listAttr) {// 遍历当前节点的所有属性 48. result.put(attr.getName(), attr.getValue()); 49. } 50. // 递归遍历当前节点所有的子节点 51. List<Element> listElement = node.elements();// 所有一级子节点的list 52. if (!listElement.isEmpty()) { 53. for (Element e : listElement) {// 遍历所有一级子节点 54. if (e.attributes().isEmpty() && e.elements().isEmpty()) // 判断一级节点是否有属性和子节点 55. result.put(e.getName(), e.getTextTrim());// 沒有则将当前节点作为上级节点的属性对待 56. else { 57. if (!result.containsKey(e.getName())) // 判断父节点是否存在该一级节点名称的属性 58. result.put(e.getName(), new JSONArray());// 没有则创建 59. ((JSONArray) result.get(e.getName())).add(elementToJSONObject(e));// 将该一级节点放入该节点名称的属性对应的值中 60. } 61. } 62. } 63. return result; 64. } 65. }
1. import lombok.Data; 2. import java.util.List; 3. 4. @Data 5. public class Items { 6. List<Item> item; 7. }
1. import lombok.Data; 2. 3. import java.util.List; 4. 5. @Data 6. public class Item { 7. private String type; 8. 9. private List<TextValue> text; 10. 11. private String name; 12. 13. private List<WebValue> web; 14. }
1. import lombok.Data; 2. 3. import java.util.List; 4. 5. @Data 6. public class Member { 7. private String toUserName; 8. private String fromUserName; 9. private String createTime; 10. private String msgType; 11. private String event; 12. private String changeType; 13. private String userID; 14. private String newUserID; 15. private String name; 16. private String department; 17. private String isLeaderInDept; 18. private String position; 19. private String mobile; 20. private String gender; 21. private String email; 22. private String status; 23. private String avatar; 24. private String alias; 25. private String telephone; 26. private String address; 27. private List<Items> extAttr; 28. }
1. import lombok.Data; 2. 3. @Data 4. public class TextValue { 5. 6. private String Value; 7. }
1. import lombok.Data; 2. 3. @Data 4. public class WebValue { 5. private String Title; 6. private String Url; 7. }