回调事件地址配置,在开发者中心配置好后,在业务系统的接口一定是非鉴权接口。具体代码可以直接copy官方文档。以下为笔者自用代码:
public Object dingCallback(
@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;
try {
log.info("begin callback:" + params);
DingCallbackCrypto dingCallbackCrypto = new DingCallbackCrypto(dingTalkConfig.getToken(), dingTalkConfig.getAesKey(), dingTalkConfig.getSuiteKey());
// 从post请求的body中获取回调信息的加密数据进行解密处理
String encrypt = body.getString("encrypt");
String plainText = dingCallbackCrypto.getDecryptMsg(signature, timestamp.toString(), nonce, encrypt);
JSONObject callBackContent = JSON.parseObject(plainText);
System.out.println(callBackContent);
// 根据回调事件类型做不同的业务处理
String eventType = callBackContent.getString("EventType");
if (Constants.EVENT_CHECK_CREATE_SUITE_URL.equals(eventType)) {
log.info("验证新创建的回调URL有效性: " + plainText);
} else if (Constants.EVENT_CHECK_UPADTE_SUITE_URL.equals(eventType)) {
log.info("验证更新回调URL有效性: " + plainText);
} else if (Constants.EVENT_SUITE_TICKET.equals(eventType)) {
// suite_ticket用于用签名形式生成accessToken(访问钉钉服务端的凭证),需要保存到应用的db。
// 钉钉会定期向本callback url推送suite_ticket新值用以提升安全性。
// 应用在获取到新的时值时,保存db成功后,返回给钉钉success加密串(如本demo的return)
log.info("应用suite_ticket数据推送: " + plainText);
refreshSuiteTicket(plainText);
} else if (Constants.EVENT_TMP_AUTH_CODE.equals(eventType)) {
// 本事件应用应该异步进行授权开通企业的初始化,目的是尽最大努力快速返回给钉钉服务端。用以提升企业管理员开通应用体验
// 即使本接口没有收到数据或者收到事件后处理初始化失败都可以后续再用户试用应用时从前端获取到corpId并拉取授权企业信息,进而初始化开通及企业。
log.info("企业授权开通应用事件: " + plainText);
} else {
// 其他类型事件处理
}
// 返回success的加密信息表示回调处理成功
return dingCallbackCrypto.getEncryptedMap("success", timestamp, nonce);
} catch (Exception e) {
//失败的情况,应用的开发者应该通过告警感知,并干预修复
log.error("process callback fail." + params, e);
return "fail";
}
}
log.info(
"应用suite_ticket数据推送: "
+ plainText);下的那个方法为笔者业务系统处理相关推送的逻辑,可按实际情况编写
/**
* 处理钉钉推送
* 数据格式参考 钉钉开发者平台
* https://open.dingtalk.com/document/isvapp/data-formats
*
* @param decryptMsg 推送json
*/
public void refreshSuiteTicket(String decryptMsg) {
JSONObject eventJson = JSON.parseObject(decryptMsg);
JSONArray bizDataList = eventJson.getJSONArray(DingTalkJsonParamConstants.bizData);
if (ObjectUtil.isEmpty(bizDataList)) {
log.info("钉钉推送的数据内容为空");
return;
}
for (Iterator<Object> iterator = bizDataList.iterator(); iterator.hasNext(); ) {
JSONObject jsonObject = (JSONObject) iterator.next();
if (!Constants.BIZ_TYPE_2.equals(jsonObject.getInteger(DingTalkJsonParamConstants.biz_type))) {
continue;
}
String bizDataStr = jsonObject.getString(DingTalkJsonParamConstants.biz_data);
String bizId = jsonObject.getString(DingTalkJsonParamConstants.biz_id);
JSONObject bizDataDTO = JSONObject.parseObject(bizDataStr);
if (null == bizDataDTO) {
continue;
}
if (DingTalkJsonParamConstants.suite_ticket.equals(bizDataDTO.getString(DingTalkJsonParamConstants.syncAction))) {
// 刷新缓存中的suiteTicket值
dingTalkService.refreshSuiteTicket(bizId, bizDataDTO.getString(DingTalkJsonParamConstants.suiteTicket));
}
}
}
public void refreshSuiteTicket(String appId, String suiteTicket) {
// 缓存周期建议比suiteTicket的有效期要长一些。 缓存6h
RBucket<String> suiteTicketBucket = redissonClient.getBucket(DingTalkCacheConstants.suiteTicket + appId);
//设置缓存 直接覆盖
suiteTicketBucket.set(suiteTicket, 21600, TimeUnit.SECONDS);
}
以上为监听ticket通知事件,相关demo代码。