Ver código fonte

Merge branch 'feature/dingding_sync_callback' into 'develop'

钉钉通讯录变更回调功能

See merge request o2oa/o2oa!1929
楼国栋 5 anos atrás
pai
commit
05259e3665
15 arquivos alterados com 930 adições e 5 exclusões
  1. 20 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Dingding.java
  2. 52 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ActionCallbackAESKey.java
  3. 80 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ActionSyncOrganizationCallbackPost.java
  4. 146 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ActionSyncOrganizationCallbackUrlRegister.java
  5. 76 5
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/DingdingAction.java
  6. 20 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/DingtalkCallbackWo.java
  7. 12 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ExceptionRegisterCallbackMessage.java
  8. 47 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/TestMain.java
  9. 47 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/DingTalkEncryptException.java
  10. 219 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/DingTalkEncryptor.java
  11. 54 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/DingTalkJsApiSingnature.java
  12. 46 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/PKCS7Padding.java
  13. 41 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/Utils.java
  14. 52 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/qiyeweixin/ActionCallbackAESKey.java
  15. 18 0
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/qiyeweixin/QiyeweixinAction.java

+ 20 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Dingding.java

@@ -40,6 +40,12 @@ public class Dingding extends ConfigObject {
 	@FieldDescribe("oapi服务器地址")
 	@FieldDescribe("oapi服务器地址")
 	private String oapiAddress;
 	private String oapiAddress;
 
 
+	@FieldDescribe("回调token")
+	private String token = "";
+
+	@FieldDescribe("回调encodingAesKey")
+	private String encodingAesKey = "";
+
 	@FieldDescribe("钉钉消息打开工作的url地址,如:http://dev.o2oa.net/x_desktop/")
 	@FieldDescribe("钉钉消息打开工作的url地址,如:http://dev.o2oa.net/x_desktop/")
 	private String workUrl = "";
 	private String workUrl = "";
 
 
@@ -311,5 +317,19 @@ public class Dingding extends ConfigObject {
 		this.attendanceSyncEnable = attendanceSyncEnable;
 		this.attendanceSyncEnable = attendanceSyncEnable;
 	}
 	}
 
 
+	public String getToken() {
+		return token;
+	}
+
+	public void setToken(String token) {
+		this.token = token;
+	}
+
+	public String getEncodingAesKey() {
+		return encodingAesKey;
+	}
 
 
+	public void setEncodingAesKey(String encodingAesKey) {
+		this.encodingAesKey = encodingAesKey;
+	}
 }
 }

+ 52 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ActionCallbackAESKey.java

@@ -0,0 +1,52 @@
+package com.x.program.center.jaxrs.dingding;
+
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import org.apache.commons.codec.binary.Base64;
+
+import java.util.UUID;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionCallbackAESKey extends BaseAction {
+
+    private static Logger logger = LoggerFactory.getLogger(ActionCallbackAESKey.class);
+
+    ActionResult<Wo> execute(EffectivePerson effectivePerson) throws Exception {
+        ActionResult<Wo> result = new ActionResult<Wo>();
+        String encodingAesKey = Base64.encodeBase64String(UUID.randomUUID().toString().replaceAll("-","").getBytes());
+        logger.info("生成钉钉回调用的 AESKey "+ encodingAesKey);
+        if (encodingAesKey.contains("=")) {
+            encodingAesKey = encodingAesKey.substring(0, encodingAesKey.lastIndexOf("="));
+        }
+        Wo wo = new Wo();
+        wo.setKey(encodingAesKey);
+        result.setData(wo);
+        return result;
+    }
+
+
+    public static class Wo extends GsonPropertyObject {
+
+
+        private static final long serialVersionUID = 6701949833103126778L;
+
+
+        @FieldDescribe("EncodingAESKey")
+        private String key;
+
+        public String getKey() {
+            return key;
+        }
+
+        public void setKey(String key) {
+            this.key = key;
+        }
+    }
+}

+ 80 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ActionSyncOrganizationCallbackPost.java

@@ -0,0 +1,80 @@
+package com.x.program.center.jaxrs.dingding;
+
+import com.google.gson.JsonElement;
+
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.program.center.ThisApplication;
+import com.x.program.center.jaxrs.dingding.encrypt.DingTalkEncryptor;
+import com.x.program.center.jaxrs.dingding.encrypt.Utils;
+
+import java.util.*;
+
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionSyncOrganizationCallbackPost extends BaseAction {
+
+    private static Logger logger = LoggerFactory.getLogger(ActionSyncOrganizationCallbackPost.class);
+
+    private List<String> tags = new ArrayList<>(Arrays.asList("user_add_org", "user_modify_org", "user_leave_org", "user_active_org", "org_dept_create", "org_dept_modify", "org_dept_remove"));
+
+    Map<String, String> execute(EffectivePerson effectivePerson, String signature, String timestamp, String nonce, JsonElement body) throws Exception {
+        String params = "signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " body:" + body;
+        logger.info(params);
+        if (Config.dingding().getEnable()) {
+            Wi wi = this.convertToWrapIn(body, Wi.class);
+            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(Config.dingding().getToken(), Config.dingding().getEncodingAesKey(), Config.dingding().getCorpId());
+            String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, wi.getEncrypt());
+            logger.info("解密后的结果:" + plainText);
+            DingtalkEvent event = gson.fromJson(plainText, DingtalkEvent.class);
+
+            if ("check_url".equals(event.getEventType())) { //检查回调地址
+                logger.info("检查url");
+            } else if (tags.contains(event.getEventType())) {
+                logger.info("通讯录变更。。。。添加定时任务的队列消息。");
+                ThisApplication.dingdingSyncOrganizationCallbackRequest.add(new Object());
+            } else {
+                logger.info("忽略的。。。。。。。。。。");
+            }
+            Map<String, String> result = dingTalkEncryptor.getEncryptedMap("success", System.currentTimeMillis(), Utils.getRandomStr(8));
+            return result;
+        }else {
+            throw new ExceptionNotPullSync();
+        }
+
+    }
+
+
+    public static class Wi extends GsonPropertyObject {
+        @FieldDescribe("加密字符串")
+        private String encrypt;
+
+        public String getEncrypt() {
+            return encrypt;
+        }
+
+        public void setEncrypt(String encrypt) {
+            this.encrypt = encrypt;
+        }
+    }
+
+    public static class DingtalkEvent extends GsonPropertyObject {
+        @FieldDescribe("事件类型")
+        private String EventType;
+
+        public String getEventType() {
+            return EventType;
+        }
+
+        public void setEventType(String eventType) {
+            EventType = eventType;
+        }
+    }
+}

+ 146 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ActionSyncOrganizationCallbackUrlRegister.java

@@ -0,0 +1,146 @@
+package com.x.program.center.jaxrs.dingding;
+
+
+import com.x.base.core.project.config.CenterServer;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.connection.HttpConnection;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WrapBoolean;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionSyncOrganizationCallbackUrlRegister extends BaseAction {
+
+    private static Logger logger = LoggerFactory.getLogger(ActionSyncOrganizationCallbackUrlRegister.class);
+
+    private List<String> tags = new ArrayList<>(Arrays.asList("user_add_org", "user_modify_org", "user_leave_org", "user_active_org", "org_dept_create", "org_dept_modify", "org_dept_remove"));
+
+
+    ActionResult<Wo> execute(EffectivePerson effectivePerson, boolean enable) throws Exception {
+        logger.info("注册钉钉回调接口。。。。。。。");
+        ActionResult<Wo> result = new ActionResult<>();
+        if (Config.dingding().getEnable()) {
+            RegisterObject registerObject = new RegisterObject();
+            registerObject.setAes_key(Config.dingding().getEncodingAesKey());
+            registerObject.setCall_back_tag(tags);
+            registerObject.setToken(Config.dingding().getToken());
+            //获取center服务器地址信息
+            CenterServer centerServer = Config.currentNode().getCenter();
+            Boolean sslEnable = centerServer.getSslEnable();
+            String host = centerServer.getProxyHost();
+            int port = centerServer.getProxyPort();
+            //回调地址
+            String callbackUrl = getApplicationUrl(sslEnable, host, port) + "/x_program_center/jaxrs/dingding/sync/organization/callback";
+            registerObject.setUrl(callbackUrl);
+            logger.info("注册回调地址 post对象:{}", registerObject.toString());
+            //钉钉回调地址注册 url post
+            String address;
+            if (enable) {
+                address = Config.dingding().getOapiAddress() + "/call_back/register_call_back?access_token=" + Config.dingding().corpAccessToken();
+            }else {
+                address = Config.dingding().getOapiAddress() + "/call_back/update_call_back?access_token=" + Config.dingding().corpAccessToken();
+            }
+            logger.info("register url :" + address);
+            DingdingMessageResp resp = HttpConnection.postAsObject(address, null, registerObject.toString(), DingdingMessageResp.class);
+            if (resp.getErrcode() != 0) {
+                throw  new ExceptionRegisterCallbackMessage(resp.getErrcode(), resp.getErrmsg());
+            }else  {
+                Wo wo = new Wo();
+                wo.setValue(true);
+                result.setData(wo);
+            }
+        }else {
+            throw new ExceptionNotPullSync();
+        }
+
+        return result;
+    }
+
+    private String getApplicationUrl(Boolean sslEnable, String host, int port) {
+        if( sslEnable ) {
+            return "https://" + host + ":" + port;
+        }else {
+            return "http://" + host + ":" + port;
+        }
+    }
+
+
+    public static class Wo extends WrapBoolean {
+    }
+
+    public static class RegisterObject extends GsonPropertyObject {
+
+        private List<String> call_back_tag;
+        private String token;
+        private String aes_key;
+        private String url;
+
+        public List<String> getCall_back_tag() {
+            return call_back_tag;
+        }
+
+        public void setCall_back_tag(List<String> call_back_tag) {
+            this.call_back_tag = call_back_tag;
+        }
+
+        public String getToken() {
+            return token;
+        }
+
+        public void setToken(String token) {
+            this.token = token;
+        }
+
+        public String getAes_key() {
+            return aes_key;
+        }
+
+        public void setAes_key(String aes_key) {
+            this.aes_key = aes_key;
+        }
+
+        public String getUrl() {
+            return url;
+        }
+
+        public void setUrl(String url) {
+            this.url = url;
+        }
+    }
+
+    public static class DingdingMessageResp {
+
+        private Integer errcode;
+        private String errmsg;
+
+        public String getErrmsg() {
+            return errmsg;
+        }
+
+        public void setErrmsg(String errmsg) {
+            this.errmsg = errmsg;
+        }
+
+
+
+        public Integer getErrcode() {
+            return errcode;
+        }
+
+        public void setErrcode(Integer errcode) {
+            this.errcode = errcode;
+        }
+
+    }
+}

+ 76 - 5
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/DingdingAction.java

@@ -1,19 +1,19 @@
 package com.x.program.center.jaxrs.dingding;
 package com.x.program.center.jaxrs.dingding;
 
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import javax.ws.rs.*;
 import javax.ws.rs.container.AsyncResponse;
 import javax.ws.rs.container.AsyncResponse;
 import javax.ws.rs.container.Suspended;
 import javax.ws.rs.container.Suspended;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 
 
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonElement;
 import com.x.base.core.project.annotation.JaxrsDescribe;
 import com.x.base.core.project.annotation.JaxrsDescribe;
 import com.x.base.core.project.annotation.JaxrsMethodDescribe;
 import com.x.base.core.project.annotation.JaxrsMethodDescribe;
+import com.x.base.core.project.annotation.JaxrsParameterDescribe;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
 import com.x.base.core.project.http.EffectivePerson;
 import com.x.base.core.project.http.HttpMediaType;
 import com.x.base.core.project.http.HttpMediaType;
@@ -22,6 +22,8 @@ import com.x.base.core.project.jaxrs.StandardJaxrsAction;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.logger.LoggerFactory;
 
 
+import java.util.Map;
+
 @Path("dingding")
 @Path("dingding")
 @JaxrsDescribe("钉钉接口")
 @JaxrsDescribe("钉钉接口")
 public class DingdingAction extends StandardJaxrsAction {
 public class DingdingAction extends StandardJaxrsAction {
@@ -63,4 +65,73 @@ public class DingdingAction extends StandardJaxrsAction {
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 	}
 	}
 
 
+
+	@JaxrsMethodDescribe(value = "获取钉钉回调的EncodingAESKey.", action = ActionCallbackAESKey.class)
+	@GET
+	@Path("get/callback/aes")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void syncOrganizationCallbackEncodingAESKeyGet(@Suspended final AsyncResponse asyncResponse,
+														  @Context HttpServletRequest request) {
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		ActionResult<ActionCallbackAESKey.Wo> result = new ActionResult<>();
+		try {
+			result = new ActionCallbackAESKey().execute(effectivePerson);
+		}catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
+	@JaxrsMethodDescribe(value = "到钉钉注册回调地址", action = ActionSyncOrganizationCallbackUrlRegister.class)
+	@GET
+	@Path("sync/organization/register/callback/{enable}")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void registerSyncOrgCallbackUrl(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+										   @JaxrsParameterDescribe("注册回调地址还是更新") @PathParam("enable") boolean enable) {
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		ActionResult<ActionSyncOrganizationCallbackUrlRegister.Wo> result = new ActionResult<>();
+		try {
+			result = new ActionSyncOrganizationCallbackUrlRegister().execute(effectivePerson, enable);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
+	/**
+	 * 钉钉回调用的post接口 需要返回固定格式的json字符串
+	 * @param asyncResponse
+	 * @param request
+	 * @param signature
+	 * @param timestamp
+	 * @param nonce
+	 * @param jsonElement
+	 */
+	@JaxrsMethodDescribe(value = "接收钉钉通讯录变更回调的接口", action = ActionSyncOrganizationCallbackPost.class)
+	@POST
+	@Path("sync/organization/callback")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void syncOrganizationCallback(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+										 @QueryParam("signature") String signature, @QueryParam("timestamp") String timestamp,
+										 @QueryParam("nonce") String nonce, JsonElement jsonElement) {
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			Map<String, String> json = new ActionSyncOrganizationCallbackPost().execute(effectivePerson, signature, timestamp, nonce, jsonElement);
+			Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+			asyncResponse.resume(Response.ok(gson.toJson(json)).build());
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			asyncResponse.resume(Response.serverError().entity("fail").build());
+		}
+
+
+
+//		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
 }
 }

+ 20 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/DingtalkCallbackWo.java

@@ -0,0 +1,20 @@
+package com.x.program.center.jaxrs.dingding;
+
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.gson.GsonPropertyObject;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class DingtalkCallbackWo extends GsonPropertyObject {
+
+    @FieldDescribe("消息体签名")
+    private String msg_signature; //消息体签名
+    @FieldDescribe("时间戳")
+    private String timeStamp;
+    @FieldDescribe("随机字符串")
+    private String nonce;
+    @FieldDescribe("字符串“success”加密值")
+    private String encrypt;
+}

+ 12 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/ExceptionRegisterCallbackMessage.java

@@ -0,0 +1,12 @@
+package com.x.program.center.jaxrs.dingding;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionRegisterCallbackMessage extends PromptException {
+
+	private static final long serialVersionUID = 4132300948670472899L;
+
+	ExceptionRegisterCallbackMessage(Integer retCode, String retMessage) {
+		super("发送钉钉消息失败,错误代码:{},错误消息:{}.", retCode, retMessage);
+	}
+}

+ 47 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/TestMain.java

@@ -0,0 +1,47 @@
+package com.x.program.center.jaxrs.dingding;
+
+
+import com.google.gson.Gson;
+import com.x.program.center.jaxrs.dingding.encrypt.DingTalkEncryptor;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class TestMain {
+
+    public static void main(String[] args) {
+        String token = "o2oa";
+        String aseKey = "Nzk5ZjFiODIxMzkyNGMyNWIyMmFiZWZkNjIzNjIxYTg";
+        String appkey = "ding21016b71e78da961";
+        String nonce = "2dffdfdfdf2222";
+        try {
+            Long timestamp = new Date().getTime();
+            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(token, aseKey, appkey);
+            Map<String, String> map = dingTalkEncryptor.getEncryptedMap("success", timestamp, nonce);
+            Gson gson = new Gson();
+            String json = gson.toJson(map);
+            System.out.println(json);
+
+            //{
+            //    "timeStamp": "1603697312745",
+            //    "msg_signature": "502b261eda47a212db163c91322f7ddecb2e99cc",
+            //    "encrypt": "QH/UQCL7dUGoPQZcBf2H2u/8ImJ+6tjDOSfHle3GmMoG/zopHbD8JpXVCBFGSUFmQ3bWXIuzwyXAZCeS2kptmA==",
+            //    "nonce": "2dffdfdfdf2222"
+            //}
+
+//            String j = dingTalkEncryptor.getDecryptMsg("111108bb8e6dbce3c9671d6fdb69d15066227608", "1783610513", "123456",
+//                    "1ojQf0NSvw2WPvW7LijxS8UvISr8pdDP+rXpPbcLGOmIBNbWetRg7IP0vdhVgkVwSoZBJeQwY2zhROsJq/HJ+q6tp1qhl9L1+ccC9ZjKs1wV5bmA9NoAWQiZ+7MpzQVq+j74rJQljdVyBdI/dGOvsnBSCxCVW0ISWX0vn9lYTuuHSoaxwCGylH9xRhYHL9bRDskBc7bO0FseHQQasdfghjkl");
+//            System.out.println(j+"点点滴滴");
+
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+}

+ 47 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/DingTalkEncryptException.java

@@ -0,0 +1,47 @@
+package com.x.program.center.jaxrs.dingding.encrypt;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class DingTalkEncryptException extends Exception {
+    public static final int SUCCESS = 0;
+    public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;
+    public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;
+    public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;
+    public static final int AES_KEY_ILLEGAL = 900004;
+    public static final int SIGNATURE_NOT_MATCH = 900005;
+    public static final int COMPUTE_SIGNATURE_ERROR = 900006;
+    public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;
+    public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;
+    public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;
+    public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;
+    private static Map<Integer, String> msgMap = new HashMap();
+    private Integer code;
+
+    public Integer getCode() {
+        return this.code;
+    }
+
+    public DingTalkEncryptException(Integer exceptionCode) {
+        super((String)msgMap.get(exceptionCode));
+        this.code = exceptionCode;
+    }
+
+    static {
+        msgMap.put(0, "成功");
+        msgMap.put(900001, "加密明文文本非法");
+        msgMap.put(900002, "加密时间戳参数非法");
+        msgMap.put(900003, "加密随机字符串参数非法");
+        msgMap.put(900005, "签名不匹配");
+        msgMap.put(900006, "签名计算失败");
+        msgMap.put(900004, "不合法的aes key");
+        msgMap.put(900007, "计算加密文字错误");
+        msgMap.put(900008, "计算解密文字错误");
+        msgMap.put(900009, "计算解密文字长度不匹配");
+        msgMap.put(900010, "计算解密文字corpid不匹配");
+    }
+}

+ 219 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/DingTalkEncryptor.java

@@ -0,0 +1,219 @@
+package com.x.program.center.jaxrs.dingding.encrypt;
+
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Field;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class DingTalkEncryptor {
+    private static final Charset CHARSET = Charset.forName("utf-8");
+    private static final Base64 base64 = new Base64();
+    private byte[] aesKey;
+    private String token;
+    private String corpId;
+    private static final Integer AES_ENCODE_KEY_LENGTH = 43;
+    private static final Integer RANDOM_LENGTH = 16;
+
+    public DingTalkEncryptor(String token, String encodingAesKey, String corpIdOrSuiteKey) throws DingTalkEncryptException {
+        if (null != encodingAesKey && encodingAesKey.length() == AES_ENCODE_KEY_LENGTH) {
+            this.token = token;
+            this.corpId = corpIdOrSuiteKey;
+            this.aesKey = Base64.decodeBase64(encodingAesKey + "=");
+        } else {
+            throw new DingTalkEncryptException(900004);
+        }
+    }
+
+    public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce) throws DingTalkEncryptException {
+        if (null == plaintext) {
+            throw new DingTalkEncryptException(900001);
+        } else if (null == timeStamp) {
+            throw new DingTalkEncryptException(900002);
+        } else if (null == nonce) {
+            throw new DingTalkEncryptException(900003);
+        } else {
+            String encrypt = this.encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);
+            String signature = this.getSignature(this.token, String.valueOf(timeStamp), nonce, encrypt);
+            Map<String, String> resultMap = new HashMap();
+            resultMap.put("msg_signature", signature);
+            resultMap.put("encrypt", encrypt);
+            resultMap.put("timeStamp", String.valueOf(timeStamp));
+            resultMap.put("nonce", nonce);
+            return resultMap;
+        }
+    }
+
+    public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg) throws DingTalkEncryptException {
+        String signature = this.getSignature(this.token, timeStamp, nonce, encryptMsg);
+        if (!signature.equals(msgSignature)) {
+            throw new DingTalkEncryptException(900006);
+        } else {
+            String result = this.decrypt(encryptMsg);
+            return result;
+        }
+    }
+
+    private String encrypt(String random, String plaintext) throws DingTalkEncryptException {
+        try {
+            byte[] randomBytes = random.getBytes(CHARSET);
+            byte[] plainTextBytes = plaintext.getBytes(CHARSET);
+            byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);
+            byte[] corpidBytes = this.corpId.getBytes(CHARSET);
+            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+            byteStream.write(randomBytes);
+            byteStream.write(lengthByte);
+            byteStream.write(plainTextBytes);
+            byteStream.write(corpidBytes);
+            byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());
+            byteStream.write(padBytes);
+            byte[] unencrypted = byteStream.toByteArray();
+            byteStream.close();
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(this.aesKey, 0, 16);
+            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
+            byte[] encrypted = cipher.doFinal(unencrypted);
+            String result = base64.encodeToString(encrypted);
+            return result;
+
+        } catch (Exception var15) {
+            throw new DingTalkEncryptException(900007);
+        }
+    }
+
+    private String decrypt(String text) throws DingTalkEncryptException {
+        byte[] originalArr;
+        byte[] networkOrder;
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES");
+            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(this.aesKey, 0, 16));
+            cipher.init(2, keySpec, iv);
+            networkOrder = Base64.decodeBase64(text);
+            originalArr = cipher.doFinal(networkOrder);
+        } catch (Exception var9) {
+            throw new DingTalkEncryptException(900008);
+        }
+
+        String plainText;
+        String fromCorpid;
+        try {
+            byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);
+            networkOrder = Arrays.copyOfRange(bytes, 16, 20);
+            int plainTextLegth = Utils.bytes2int(networkOrder);
+            plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);
+            fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);
+        } catch (Exception var8) {
+            throw new DingTalkEncryptException(900009);
+        }
+
+        if (!fromCorpid.equals(this.corpId)) {
+            throw new DingTalkEncryptException(900010);
+        } else {
+            return plainText;
+        }
+    }
+
+    public String getSignature(String token, String timestamp, String nonce, String encrypt) throws DingTalkEncryptException {
+        try {
+            String[] array = new String[]{token, timestamp, nonce, encrypt};
+            Arrays.sort(array);
+            StringBuffer sb = new StringBuffer();
+
+            for(int i = 0; i < 4; ++i) {
+                sb.append(array[i]);
+            }
+
+            String str = sb.toString();
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(str.getBytes());
+            byte[] digest = md.digest();
+            StringBuffer hexstr = new StringBuffer();
+            String shaHex = "";
+
+            for(int i = 0; i < digest.length; ++i) {
+                shaHex = Integer.toHexString(digest[i] & 255);
+                if (shaHex.length() < 2) {
+                    hexstr.append(0);
+                }
+
+                hexstr.append(shaHex);
+            }
+
+            return hexstr.toString();
+        } catch (Exception var13) {
+            throw new DingTalkEncryptException(900006);
+        }
+    }
+
+    private static void RemoveCryptographyRestrictions() throws Exception {
+        Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");
+        Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");
+        Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");
+        if (jceSecurity != null) {
+            setFinalStaticValue(jceSecurity, "isRestricted", false);
+            PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);
+            if (cryptoPermissions != null) {
+                Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);
+                map.clear();
+            }
+
+            if (cryptoAllPermission != null) {
+                Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);
+                defaultPolicy.add(permission);
+            }
+        }
+
+    }
+
+    private static Class<?> getClazz(String className) {
+        Class clazz = null;
+
+        try {
+            clazz = Class.forName(className);
+        } catch (Exception var3) {
+        }
+
+        return clazz;
+    }
+
+    private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {
+        Field field = srcClazz.getDeclaredField(fieldName);
+        field.setAccessible(true);
+        Field modifiersField = Field.class.getDeclaredField("modifiers");
+        modifiersField.setAccessible(true);
+        modifiersField.setInt(field, field.getModifiers() & -17);
+        field.set((Object)null, newValue);
+    }
+
+    private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {
+        Field field = srcClazz.getDeclaredField(fieldName);
+        field.setAccessible(true);
+        return dstClazz.cast(field.get(owner));
+    }
+
+    static {
+        try {
+            Security.setProperty("crypto.policy", "limited");
+            RemoveCryptographyRestrictions();
+        } catch (Exception var1) {
+        }
+
+    }
+}

+ 54 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/DingTalkJsApiSingnature.java

@@ -0,0 +1,54 @@
+package com.x.program.center.jaxrs.dingding.encrypt;
+
+import java.security.MessageDigest;
+import java.util.Formatter;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class DingTalkJsApiSingnature {
+    public DingTalkJsApiSingnature() {
+    }
+
+    public static String getJsApiSingnature(String url, String nonce, Long timeStamp, String jsTicket) throws DingTalkEncryptException {
+        String plainTex = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonce + "&timestamp=" + timeStamp + "&url=" + url;
+        System.out.println(plainTex);
+        String signature = "";
+
+        try {
+            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
+            crypt.reset();
+            crypt.update(plainTex.getBytes("UTF-8"));
+            signature = byteToHex(crypt.digest());
+            return signature;
+        } catch (Exception var7) {
+            throw new DingTalkEncryptException(900006);
+        }
+    }
+
+    private static String byteToHex(byte[] hash) {
+        Formatter formatter = new Formatter();
+        byte[] var2 = hash;
+        int var3 = hash.length;
+
+        for(int var4 = 0; var4 < var3; ++var4) {
+            byte b = var2[var4];
+            formatter.format("%02x", b);
+        }
+
+        String result = formatter.toString();
+        formatter.close();
+        return result;
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("=" + getJsApiSingnature("http://test4.weixin.wtoip.com/wtoip/dingding/dzmp", "abcde12345", 1463132072L, "M95mXz9E4Wy9LjWBIYZdkXOhM7KRvXr5Cq2Yz521gi7d3rnqEY4JO2FFsjLgJC8b5G7ajnJARnidJVYl4hjaXD"));
+        String url = "http://test4.weixin.wtoip.com:80/wtoip/dingding/dzmp";
+        String nonce = "abcde12345";
+        Long timeStamp = 1463125744L;
+        String tikcet = "gUsHOoPPzLVZKVkClnESg88m7qMV4c0Ys9VGsMigqzZU7gA8PeoNzHODmYPZ85TYuoZryXuqEUFlXLN1OPEixm";
+        String jsApiSingnature = getJsApiSingnature(url, nonce, timeStamp, tikcet);
+        System.err.println(jsApiSingnature + ", equals = " + jsApiSingnature.equals("d14dfc1d0d98cad2438e664723e8a9d8633b443f"));
+    }
+}

+ 46 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/PKCS7Padding.java

@@ -0,0 +1,46 @@
+package com.x.program.center.jaxrs.dingding.encrypt;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class PKCS7Padding {
+    private static final Charset CHARSET = Charset.forName("utf-8");
+    private static final int BLOCK_SIZE = 32;
+
+    public PKCS7Padding() {
+    }
+
+    public static byte[] getPaddingBytes(int count) {
+        int amountToPad = 32 - count % 32;
+        if (amountToPad == 0) {
+            amountToPad = 32;
+        }
+
+        char padChr = chr(amountToPad);
+        String tmp = new String();
+
+        for(int index = 0; index < amountToPad; ++index) {
+            tmp = tmp + padChr;
+        }
+
+        return tmp.getBytes(CHARSET);
+    }
+
+    public static byte[] removePaddingBytes(byte[] decrypted) {
+        int pad = decrypted[decrypted.length - 1];
+        if (pad < 1 || pad > 32) {
+            pad = 0;
+        }
+
+        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+    }
+
+    private static char chr(int a) {
+        byte target = (byte)(a & 255);
+        return (char)target;
+    }
+}

+ 41 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/dingding/encrypt/Utils.java

@@ -0,0 +1,41 @@
+package com.x.program.center.jaxrs.dingding.encrypt;
+
+import java.util.Random;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class Utils {
+    public Utils() {
+    }
+
+    public static String getRandomStr(int count) {
+        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        Random random = new Random();
+        StringBuffer sb = new StringBuffer();
+
+        for(int i = 0; i < count; ++i) {
+            int number = random.nextInt(base.length());
+            sb.append(base.charAt(number));
+        }
+
+        return sb.toString();
+    }
+
+    public static byte[] int2Bytes(int count) {
+        byte[] byteArr = new byte[]{(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255), (byte)(count & 255)};
+        return byteArr;
+    }
+
+    public static int bytes2int(byte[] byteArr) {
+        int count = 0;
+
+        for(int i = 0; i < 4; ++i) {
+            count <<= 8;
+            count |= byteArr[i] & 255;
+        }
+
+        return count;
+    }
+}

+ 52 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/qiyeweixin/ActionCallbackAESKey.java

@@ -0,0 +1,52 @@
+package com.x.program.center.jaxrs.qiyeweixin;
+
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import org.apache.commons.codec.binary.Base64;
+
+import java.util.UUID;
+
+/**
+ * Created by fancyLou on 2020-10-26.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionCallbackAESKey extends BaseAction {
+
+    private static Logger logger = LoggerFactory.getLogger(ActionCallbackAESKey.class);
+
+    ActionResult<Wo> execute(EffectivePerson effectivePerson) throws Exception {
+        ActionResult<Wo> result = new ActionResult<Wo>();
+        String encodingAesKey = Base64.encodeBase64String(UUID.randomUUID().toString().replaceAll("-","").getBytes());
+        logger.info("生成企业微信回调用的 AESKey "+ encodingAesKey);
+        if (encodingAesKey.contains("=")) {
+            encodingAesKey = encodingAesKey.substring(0, encodingAesKey.lastIndexOf("="));
+        }
+        Wo wo = new Wo();
+        wo.setKey(encodingAesKey);
+        result.setData(wo);
+        return result;
+    }
+
+
+    public static class Wo extends GsonPropertyObject {
+
+
+        private static final long serialVersionUID = 6701949833103126778L;
+
+
+        @FieldDescribe("EncodingAESKey")
+        private String key;
+
+        public String getKey() {
+            return key;
+        }
+
+        public void setKey(String key) {
+            this.key = key;
+        }
+    }
+}

+ 18 - 0
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/qiyeweixin/QiyeweixinAction.java

@@ -64,6 +64,24 @@ public class QiyeweixinAction extends StandardJaxrsAction {
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 	}
 	}
 
 
+	@JaxrsMethodDescribe(value = "获取企业微信回调的EncodingAESKey.", action = ActionCallbackAESKey.class)
+	@GET
+	@Path("get/callback/aes")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void syncOrganizationCallbackEncodingAESKeyGet(@Suspended final AsyncResponse asyncResponse,
+														  @Context HttpServletRequest request) {
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		ActionResult<ActionCallbackAESKey.Wo> result = new ActionResult<>();
+		try {
+			result = new ActionCallbackAESKey().execute(effectivePerson);
+		}catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
 //	@Path("sync/organization/callback")
 //	@Path("sync/organization/callback")
 //	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 //	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 //	@Consumes(MediaType.APPLICATION_JSON)
 //	@Consumes(MediaType.APPLICATION_JSON)