|
@@ -20,6 +20,7 @@ import com.dingtalk.open.app.api.OpenDingTalkStreamClientBuilder;
|
|
|
import com.dingtalk.open.app.api.message.GenericOpenDingTalkEvent;
|
|
|
import com.dingtalk.open.app.api.security.AuthClientCredential;
|
|
|
import com.dingtalk.open.app.stream.protocol.event.EventAckStatus;
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
import com.ruoyi.common.constant.Constants;
|
|
|
import com.ruoyi.common.core.domain.AjaxResult;
|
|
|
import com.ruoyi.common.core.domain.entity.SysRole;
|
|
@@ -44,18 +45,17 @@ import com.taobao.api.ApiException;
|
|
|
import com.taobao.api.TaobaoResponse;
|
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Controller;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Optional;
|
|
|
+import java.util.*;
|
|
|
|
|
|
import static com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.log;
|
|
|
+import static com.ruoyi.web.controller.dingding.CallbackConstant.*;
|
|
|
|
|
|
/**
|
|
|
* <p>DingLoginController 此类用于:钉钉企业内部应用免登(H5微应用)</p>
|
|
@@ -64,6 +64,7 @@ import static com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.log;
|
|
|
@Api(value = "dingAuthController", tags = "钉钉企业内部应用免登(H5微应用)")
|
|
|
@Controller
|
|
|
@RequestMapping(value = "/ding")
|
|
|
+@Slf4j
|
|
|
public class DingAuthController {
|
|
|
@Resource
|
|
|
private DingAppConfig dingAppConfig;
|
|
@@ -112,44 +113,100 @@ public class DingAuthController {
|
|
|
*/
|
|
|
private static final String EVENT_TMP_AUTH_CODE = "tmp_auth_code";
|
|
|
|
|
|
- @PostMapping(value = "dingCallback2")
|
|
|
- public Object dingCallback2(
|
|
|
- @RequestParam(value = "signature") String signature,
|
|
|
- @RequestParam(value = "timestamp") Long timestamp,
|
|
|
- @RequestParam(value = "nonce") String nonce,
|
|
|
- @RequestBody(required = false) JSONObject body
|
|
|
- ) {
|
|
|
- String params = "signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " body:" + body;
|
|
|
- DingTalkEncryptor dingTalkEncryptor = null;
|
|
|
+ @PostMapping(value = "/callback1")
|
|
|
+ public Map<String, String> callback1(@RequestParam String signature,
|
|
|
+ @RequestParam String timestamp,
|
|
|
+ @RequestParam String nonce,
|
|
|
+ @RequestBody JsonNode encryptNode) {
|
|
|
+ String encryptMsg = encryptNode.get("encrypt").textValue();
|
|
|
+ String plainText = this.decryptText(signature, timestamp, nonce, encryptMsg);
|
|
|
+ JSONObject callBackContent = JSON.parseObject(plainText);
|
|
|
+
|
|
|
+ //进入回调事件分支选择
|
|
|
+ Map<String, String> resultMap = caseProcess(callBackContent);
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+ public String decryptText(String signature, String timestamp, String nonce, String encryptMsg) {
|
|
|
+ String plainText = "";
|
|
|
try {
|
|
|
- dingTalkEncryptor = new DingTalkEncryptor(Constant.TOKEN, Constant.ENCODING_AES_KEY, Constant.SUITE_KEY);
|
|
|
- // 从post请求的body中获取回调信息的加密数据进行解密处理,消息加解密参见下文
|
|
|
- String encrypt = body.getString("encrypt");
|
|
|
- String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp.toString(), nonce, encrypt);
|
|
|
- log.info("应用suite_ticket数据推送: " + plainText);
|
|
|
- JSONObject callBackContent = JSON.parseObject(plainText);
|
|
|
- log.info("begin callback encrypt:" + callBackContent.toString());
|
|
|
- // 根据回调事件类型做不同的业务处理
|
|
|
- String eventType = callBackContent.getString("EventType");
|
|
|
- //TODO 将数据持久化
|
|
|
- return plainText;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("process callback fail." + params, e);
|
|
|
- return "fail";
|
|
|
+ DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(Constant.TOKEN, Constant.ENCODING_AES_KEY, Constant.SUITE_KEY);
|
|
|
+ plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
|
|
|
+ } catch (DingTalkEncryptException e) {
|
|
|
+ log.error("钉钉消息体解密错误, signature: {}, timestamp: {}, nonce: {}, encryptMsg: {}, e: {}", signature, timestamp, nonce, encryptMsg, e);
|
|
|
}
|
|
|
+ log.debug("钉钉消息体解密, signature: {}, timestamp: {}, nonce: {}, encryptMsg: {}, 解密结果: {}", signature, timestamp, nonce, encryptMsg, plainText);
|
|
|
+ return plainText;
|
|
|
}
|
|
|
- @GetMapping(value = "send")
|
|
|
- public void send() {
|
|
|
- try {
|
|
|
|
|
|
- } catch (Exception e) {
|
|
|
- log.info("start fail."+e.getMessage());
|
|
|
- throw new RuntimeException(e);
|
|
|
+ private Map<String, String> caseProcess(JSONObject plainNode) {
|
|
|
+ Map<String, String> resultMap = new LinkedHashMap<>();
|
|
|
+ String eventType = plainNode.getString("EventType");
|
|
|
+ switch (eventType) {
|
|
|
+ case SUITE_TICKET_CALLBACK_URL_VALIDATE:
|
|
|
+ log.info("[callback] 验证回调地址有效性质:{}", plainNode);
|
|
|
+ resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
|
|
|
+ break;
|
|
|
+ case TEMP_AUTH_CODE_ACTIVE:
|
|
|
+ log.info("[callback] 企业开通授权:{}", plainNode);
|
|
|
+
|
|
|
+ resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
|
|
|
+ break;
|
|
|
+ case SUITE_RELIEVE:
|
|
|
+ log.info("[callback] 企业解除授权:{}", plainNode);
|
|
|
+ resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
|
|
|
+ break;
|
|
|
+ case CHECK_UPDATE_SUITE_URL:
|
|
|
+ log.info("[callback] 在开发者后台修改回调地址:" + plainNode);
|
|
|
+ resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
|
|
|
+ break;
|
|
|
+ case CHECK_CREATE_SUITE_URL:
|
|
|
+ log.info("[callback] 检查钉钉向回调URL POST数据解密后是否成功:" + plainNode);
|
|
|
+ resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
|
|
|
+ break;
|
|
|
+ case CONTACT_CHANGE_AUTH:
|
|
|
+ log.info("[callback] 通讯录授权范围变更事件:" + plainNode);
|
|
|
+ break;
|
|
|
+ case ORG_MICRO_APP_STOP:
|
|
|
+ log.info("[callback] 停用应用:" + plainNode);
|
|
|
+ break;
|
|
|
+ case ORG_MICRO_APP_RESTORE:
|
|
|
+ log.info("[callback] 启用应用:" + plainNode);
|
|
|
+ break;
|
|
|
+ case MARKET_BUY:
|
|
|
+ log.info("[callback] 用户下单购买事件:" + plainNode);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log.info("[callback] 未知事件: {} , 内容: {}", eventType, plainNode);
|
|
|
+ resultMap = encryptText("事件类型未定义, 请联系应用提供方!" + eventType);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+ public Map<String, String> encryptText(String text) {
|
|
|
+ Map<String, String> resultMap = new LinkedHashMap<>();
|
|
|
+ try {
|
|
|
+ DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(Constant.TOKEN, Constant.ENCODING_AES_KEY, Constant.SUITE_KEY);
|
|
|
+ resultMap = dingTalkEncryptor.getEncryptedMap(text, System.currentTimeMillis(),getRandomStr(8));
|
|
|
+ } catch (DingTalkEncryptException e) {
|
|
|
+ log.error("钉钉消息体加密,text: {}, e: {}", text, e);
|
|
|
}
|
|
|
+ log.debug("钉钉消息体加密,text: {}, resultMap: {}", text, resultMap);
|
|
|
+ return resultMap;
|
|
|
}
|
|
|
+ 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));
|
|
|
+ }
|
|
|
|
|
|
- @PostMapping(value = "callback")
|
|
|
- public Object dingCallback(
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+ @PostMapping(value = "callback2")
|
|
|
+ public Object callback2(
|
|
|
@RequestParam(value = "signature") String signature,
|
|
|
@RequestParam(value = "timestamp") Long timestamp,
|
|
|
@RequestParam(value = "nonce") String nonce,
|
|
@@ -195,7 +252,32 @@ public class DingAuthController {
|
|
|
return "fail";
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+ @PostMapping(value = "callback3")
|
|
|
+ public Object callback3(
|
|
|
+ @RequestParam(value = "signature") String signature,
|
|
|
+ @RequestParam(value = "timestamp") Long timestamp,
|
|
|
+ @RequestParam(value = "nonce") String nonce,
|
|
|
+ @RequestBody(required = false) JSONObject body
|
|
|
+ ) {
|
|
|
+ String params = "signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " body:" + body;
|
|
|
+ DingTalkEncryptor dingTalkEncryptor = null;
|
|
|
+ try {
|
|
|
+ dingTalkEncryptor = new DingTalkEncryptor(Constant.TOKEN, Constant.ENCODING_AES_KEY, Constant.SUITE_KEY);
|
|
|
+ // 从post请求的body中获取回调信息的加密数据进行解密处理,消息加解密参见下文
|
|
|
+ String encrypt = body.getString("encrypt");
|
|
|
+ String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp.toString(), nonce, encrypt);
|
|
|
+ log.info("应用suite_ticket数据推送: " + plainText);
|
|
|
+ JSONObject callBackContent = JSON.parseObject(plainText);
|
|
|
+ log.info("begin callback encrypt:" + callBackContent.toString());
|
|
|
+ // 根据回调事件类型做不同的业务处理
|
|
|
+ String eventType = callBackContent.getString("EventType");
|
|
|
+ //TODO 将数据持久化
|
|
|
+ return plainText;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("process callback fail." + params, e);
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+ }
|
|
|
/**
|
|
|
* 钉钉用户登录,显示当前登录用户的userId和名称
|
|
|
*
|