REQ-2211: 获取钉钉config
This commit is contained in:
parent
075f64ca2c
commit
9d21a6c713
@ -0,0 +1,17 @@
|
|||||||
|
package cn.axzo.riven.client.feign;
|
||||||
|
|
||||||
|
import cn.axzo.riven.client.req.GetDingDingConfigReq;
|
||||||
|
import cn.axzo.riven.client.res.GetDingDingConfigRes;
|
||||||
|
import cn.azxo.framework.common.model.CommonResponse;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yangchen@axzo.cn
|
||||||
|
*/
|
||||||
|
@FeignClient(name = "riven", url = "${axzo.service.riven:http://riven:8080}")
|
||||||
|
public interface DingDingApi {
|
||||||
|
@PostMapping("/api/getDingDingConfig")
|
||||||
|
CommonResponse<GetDingDingConfigRes> getDingDingConfig(@RequestBody GetDingDingConfigReq req);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package cn.axzo.riven.client.req;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yangchen@axzo.cn
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GetDingDingConfigReq {
|
||||||
|
/**
|
||||||
|
* 目前后端写死
|
||||||
|
*/
|
||||||
|
private Long ouId;
|
||||||
|
/**
|
||||||
|
* 当前网页的URL,不包含#及其后面部分
|
||||||
|
*/
|
||||||
|
private String url;
|
||||||
|
/**
|
||||||
|
* 企业ID
|
||||||
|
*/
|
||||||
|
private String appId;
|
||||||
|
/**
|
||||||
|
* 选填。0表示微应用的jsapi,1表示服务窗的jsapi;不填默认为0。该参数从dingtalk.js的0.8.3版本开始支持
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
/**
|
||||||
|
* 必填,需要使用的jsapi列表,注意:不要带dd。
|
||||||
|
*/
|
||||||
|
private List<String> jsApiList;
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package cn.axzo.riven.client.res;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yangchen@axzo.cn
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GetDingDingConfigRes {
|
||||||
|
/**
|
||||||
|
* 授权企业的agentid
|
||||||
|
*/
|
||||||
|
private String appId;
|
||||||
|
/**
|
||||||
|
* 企业ID
|
||||||
|
*/
|
||||||
|
private String corpId;
|
||||||
|
/**
|
||||||
|
* 生成签名的时间戳
|
||||||
|
*/
|
||||||
|
private Long timeStamp;
|
||||||
|
/**
|
||||||
|
* 自定义固定字符串
|
||||||
|
*/
|
||||||
|
private String nonceStr;
|
||||||
|
/**
|
||||||
|
* 签名
|
||||||
|
*/
|
||||||
|
private String signature;
|
||||||
|
/**
|
||||||
|
* 0表示微应用的jsapi,1表示服务窗的jsapi;不填默认为0。该参数从dingtalk.js的0.8.3版本开始支持
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
/**
|
||||||
|
* 需要使用的jsapi列表,注意:不要带dd
|
||||||
|
*/
|
||||||
|
private List<String> jsApiList;
|
||||||
|
}
|
||||||
@ -8,5 +8,9 @@ public class RedisKeyConstant {
|
|||||||
* 企业内部应用的accessToken
|
* 企业内部应用的accessToken
|
||||||
*/
|
*/
|
||||||
public static final String DING_DING_V2_ACCESS_TOKEN_KEY = "DING:DING:V2:ACCESS_TOKEN_KEY:%s";
|
public static final String DING_DING_V2_ACCESS_TOKEN_KEY = "DING:DING:V2:ACCESS_TOKEN_KEY:%s";
|
||||||
|
/**
|
||||||
|
* 获取jsapiTicket
|
||||||
|
*/
|
||||||
|
public static final String DING_DING_V2_JSAPI_TICKET = "DING:DING:V2:JSAPI_TICKET:%s";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,87 @@
|
|||||||
|
package cn.axzo.riven.common.util;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yangchen@axzo.cn
|
||||||
|
*/
|
||||||
|
public class DdConfigSign {
|
||||||
|
/**
|
||||||
|
* 计算dd.config的签名参数
|
||||||
|
*
|
||||||
|
* @param jsticket 通过微应用appKey获取的jsticket
|
||||||
|
* @param nonceStr 自定义固定字符串
|
||||||
|
* @param timeStamp 当前时间戳
|
||||||
|
* @param url 调用dd.config的当前页面URL
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static String sign(String jsticket, String nonceStr, long timeStamp, String url) throws Exception {
|
||||||
|
String plain = "jsapi_ticket=" + jsticket + "&noncestr=" + nonceStr + "×tamp=" + timeStamp
|
||||||
|
+ "&url=" + decodeUrl(url);
|
||||||
|
try {
|
||||||
|
MessageDigest sha1 = MessageDigest.getInstance("SHA-256");
|
||||||
|
sha1.reset();
|
||||||
|
sha1.update(plain.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return byteToHex(sha1.digest());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new Exception(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字节数组转化成十六进制字符串
|
||||||
|
*/
|
||||||
|
private static String byteToHex(final byte[] hash) {
|
||||||
|
Formatter formatter = new Formatter();
|
||||||
|
for (byte b : hash) {
|
||||||
|
formatter.format("%02x", b);
|
||||||
|
}
|
||||||
|
String result = formatter.toString();
|
||||||
|
formatter.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 因为ios端上传递的url是encode过的,android是原始的url。开发者使用的也是原始url,
|
||||||
|
* 所以需要把参数进行一般urlDecode
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static String decodeUrl(String url) throws Exception {
|
||||||
|
URL urler = new URL(url);
|
||||||
|
StringBuilder urlBuffer = new StringBuilder();
|
||||||
|
urlBuffer.append(urler.getProtocol());
|
||||||
|
urlBuffer.append(":");
|
||||||
|
if (urler.getAuthority() != null && urler.getAuthority().length() > 0) {
|
||||||
|
urlBuffer.append("//");
|
||||||
|
urlBuffer.append(urler.getAuthority());
|
||||||
|
}
|
||||||
|
if (urler.getPath() != null) {
|
||||||
|
urlBuffer.append(urler.getPath());
|
||||||
|
}
|
||||||
|
if (urler.getQuery() != null) {
|
||||||
|
urlBuffer.append('?');
|
||||||
|
urlBuffer.append(URLDecoder.decode(urler.getQuery(), "utf-8"));
|
||||||
|
}
|
||||||
|
return urlBuffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package cn.axzo.riven.controller;
|
||||||
|
|
||||||
|
import cn.axzo.riven.client.feign.DingDingApi;
|
||||||
|
import cn.axzo.riven.client.req.GetDingDingConfigReq;
|
||||||
|
import cn.axzo.riven.client.res.GetDingDingConfigRes;
|
||||||
|
import cn.axzo.riven.service.DingDingService;
|
||||||
|
import cn.azxo.framework.common.model.CommonResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yangchen@axzo.cn
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DingDingController implements DingDingApi {
|
||||||
|
private final DingDingService dingDingService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResponse<GetDingDingConfigRes> getDingDingConfig(GetDingDingConfigReq req) {
|
||||||
|
return CommonResponse.success(dingDingService.getDingDingConfig(req));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,10 +4,13 @@ import cn.axzo.riven.common.constants.RedisKeyConstant;
|
|||||||
import cn.axzo.riven.common.util.Throws;
|
import cn.axzo.riven.common.util.Throws;
|
||||||
import cn.azxo.framework.common.utils.LogUtil;
|
import cn.azxo.framework.common.utils.LogUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketResponse;
|
||||||
|
import com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketResponseBody;
|
||||||
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
|
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
|
||||||
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
|
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
|
||||||
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponseBody;
|
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponseBody;
|
||||||
import com.aliyun.tea.TeaException;
|
import com.aliyun.tea.TeaException;
|
||||||
|
import com.aliyun.teautil.models.RuntimeOptions;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
@ -53,6 +56,28 @@ public class DingDingNewSdkManger {
|
|||||||
return res.getBody();
|
return res.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取jsapiTicket
|
||||||
|
*/
|
||||||
|
public CreateJsapiTicketResponseBody createJsapiTicket(String appKey, String accessToken) {
|
||||||
|
String key = String.format(RedisKeyConstant.DING_DING_V2_JSAPI_TICKET, appKey);
|
||||||
|
String tokenStr = redisTemplate.opsForValue().get(key);
|
||||||
|
if (StringUtils.hasText(tokenStr)) {
|
||||||
|
return JSONUtil.toBean(tokenStr, CreateJsapiTicketResponseBody.class);
|
||||||
|
}
|
||||||
|
com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketHeaders createJsapiTicketHeaders = new com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketHeaders();
|
||||||
|
createJsapiTicketHeaders.xAcsDingtalkAccessToken = accessToken;
|
||||||
|
try {
|
||||||
|
CreateJsapiTicketResponse res = dingTalkOauthClient.createJsapiTicketWithOptions(createJsapiTicketHeaders, new RuntimeOptions());
|
||||||
|
String data = JSONUtil.toJsonStr(res.getBody());
|
||||||
|
redisTemplate.opsForValue().set(key, data, res.getBody().expireIn - 200L, TimeUnit.SECONDS);
|
||||||
|
return res.getBody();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logErr("createJsapiTicket", e);
|
||||||
|
throw Throws.bizDefaultException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void logErr(String api, Exception e) {
|
public void logErr(String api, Exception e) {
|
||||||
TeaException err = new TeaException(e.getMessage(), e);
|
TeaException err = new TeaException(e.getMessage(), e);
|
||||||
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
|
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
package cn.axzo.riven.service;
|
||||||
|
|
||||||
|
import cn.axzo.framework.domain.web.code.BaseCode;
|
||||||
|
import cn.axzo.riven.client.req.GetDingDingConfigReq;
|
||||||
|
import cn.axzo.riven.client.res.GetDingDingConfigRes;
|
||||||
|
import cn.axzo.riven.common.util.DdConfigSign;
|
||||||
|
import cn.axzo.riven.common.util.Throws;
|
||||||
|
import cn.axzo.riven.manger.DingDingNewSdkManger;
|
||||||
|
import cn.axzo.riven.repository.entity.ThirdPartyCredential;
|
||||||
|
import cn.axzo.riven.repository.service.ThirdPartyCredentialDao;
|
||||||
|
import cn.azxo.framework.common.utils.LogUtil;
|
||||||
|
import com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketResponseBody;
|
||||||
|
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponseBody;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yangchen@axzo.cn
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class DingDingService {
|
||||||
|
@Autowired
|
||||||
|
private ThirdPartyCredentialDao thirdPartyCredentialDao;
|
||||||
|
@Autowired
|
||||||
|
private DingDingNewSdkManger dingDingNewSdkManger;
|
||||||
|
|
||||||
|
public GetDingDingConfigRes getDingDingConfig(GetDingDingConfigReq req) {
|
||||||
|
ThirdPartyCredential dingTalk = thirdPartyCredentialDao.getByOuIdAndChannel(req.getOuId(), "DingTalk");
|
||||||
|
if (dingTalk == null) {
|
||||||
|
throw Throws.bizException(BaseCode.FORBIDDEN, "企业未配置钉钉");
|
||||||
|
}
|
||||||
|
GetAccessTokenResponseBody accessToken = dingDingNewSdkManger.getAccessToken(dingTalk.getAppKey(), dingTalk.getAppSecret());
|
||||||
|
CreateJsapiTicketResponseBody jsapiTicket = dingDingNewSdkManger.createJsapiTicket(dingTalk.getAppKey(), accessToken.getAccessToken());
|
||||||
|
String randomStr = DdConfigSign.getRandomStr(10);
|
||||||
|
long ts = System.currentTimeMillis() / 1000;
|
||||||
|
String sign;
|
||||||
|
try {
|
||||||
|
sign = DdConfigSign.sign(jsapiTicket.jsapiTicket, randomStr, ts, req.getUrl());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error(LogUtil.ErrorLevel.P0, LogUtil.ErrorType.ERROR_THIRD_SERVICE, "获取钉钉配置计算签名异常", e);
|
||||||
|
throw Throws.bizDefaultException();
|
||||||
|
}
|
||||||
|
GetDingDingConfigRes getDingDingConfigRes = new GetDingDingConfigRes();
|
||||||
|
getDingDingConfigRes.setAppId(req.getAppId());
|
||||||
|
getDingDingConfigRes.setCorpId(dingTalk.getAppKey());
|
||||||
|
getDingDingConfigRes.setTimeStamp(ts);
|
||||||
|
getDingDingConfigRes.setNonceStr(randomStr);
|
||||||
|
getDingDingConfigRes.setSignature(sign);
|
||||||
|
getDingDingConfigRes.setType(req.getType());
|
||||||
|
getDingDingConfigRes.setJsApiList(req.getJsApiList());
|
||||||
|
|
||||||
|
return getDingDingConfigRes;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user