|
@@ -0,0 +1,305 @@
|
|
|
+package com.dragoninfo.dcuc.auth.business.impl.zerotrust;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.lang.Assert;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.dragoninfo.dcuc.auth.api.enums.zerotrust.ZeroTrustBusinessRespEnum;
|
|
|
+import com.dragoninfo.dcuc.auth.api.vo.ResultRespVO;
|
|
|
+import com.dragoninfo.dcuc.auth.business.zerotrust.ITokenRemoteCallBusiness;
|
|
|
+import com.dragoninfo.dcuc.auth.config.zerotrust.DcucAuthZerotrustConfig;
|
|
|
+import com.dragoninfo.dcuc.auth.token.vo.AppTokenInfoRespVO;
|
|
|
+import com.dragoninfo.dcuc.auth.token.vo.UserTokenInfoRespVO;
|
|
|
+import com.dragoninfo.dcuc.auth.token.vo.ZeroTrustAppTokenInfoReqVO;
|
|
|
+import com.dragoninfo.dcuc.auth.token.vo.ZeroTrustUserTokenInfoReqVO;
|
|
|
+import com.dragoninfo.dcuc.common.utils.LangUtil;
|
|
|
+import com.dragonsoft.duceap.commons.util.json.JsonUtils;
|
|
|
+import com.dragonsoft.smtools.loader.SMFactory;
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import lombok.SneakyThrows;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.HttpMethod;
|
|
|
+import org.springframework.http.RequestEntity;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import java.net.URI;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.Locale;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * <p>
|
|
|
+ *
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @author huangzqa
|
|
|
+ * @date 2023/6/7
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class TokenRemoteCallBusinessImpl implements ITokenRemoteCallBusiness {
|
|
|
+
|
|
|
+ private RestTemplate restTemplate;
|
|
|
+
|
|
|
+ private ObjectMapper objectMapper;
|
|
|
+
|
|
|
+ private SMFactory smFactory;
|
|
|
+
|
|
|
+ private DcucAuthZerotrustConfig zerotrustConfig;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ public void setObjectMapper(ObjectMapper objectMapper) {
|
|
|
+ this.objectMapper = objectMapper;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ public void setZerotrustConfig(DcucAuthZerotrustConfig zerotrustConfig) {
|
|
|
+ this.zerotrustConfig = zerotrustConfig;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ public void setSmFactory(SMFactory smFactory) {
|
|
|
+ this.smFactory = smFactory;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ public void setRestTemplate(RestTemplate restTemplate) {
|
|
|
+ this.restTemplate = restTemplate;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public UserTokenInfoRespVO getUserTokenInfo(String useTokenId) {
|
|
|
+ Assert.notBlank(useTokenId);
|
|
|
+ String requestName = "获取用户令牌信息";
|
|
|
+
|
|
|
+ ZeroTrustUserTokenInfoReqVO zeroTrustUserTokenInfoReqVO = new ZeroTrustUserTokenInfoReqVO();
|
|
|
+ zeroTrustUserTokenInfoReqVO.setUserTokenId(useTokenId);
|
|
|
+
|
|
|
+ log.info("{} 请求 :{}", requestName, JsonUtils.toJSONString(zeroTrustUserTokenInfoReqVO));
|
|
|
+
|
|
|
+ String reqUrl = zerotrustConfig.getUserTokenQueryUrl();
|
|
|
+ RequestEntity<ZeroTrustUserTokenInfoReqVO> httpEntity = new RequestEntity<>(zeroTrustUserTokenInfoReqVO, HttpMethod.POST, URI.create(reqUrl));
|
|
|
+
|
|
|
+ TypeReference<ResultRespVO<UserTokenInfoRespVO>> parameterizedTypeReference =
|
|
|
+ new TypeReference<ResultRespVO<UserTokenInfoRespVO>>() {
|
|
|
+ };
|
|
|
+
|
|
|
+ ResponseEntity<String> responseEntity = restTemplate.exchange(httpEntity, String.class);
|
|
|
+ log.info("{}返回参数 :{}", requestName, JsonUtils.toJSONString(responseEntity));
|
|
|
+
|
|
|
+ if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
|
|
+ String responseEntityJsonBody = responseEntity.getBody();
|
|
|
+
|
|
|
+ ResultRespVO<UserTokenInfoRespVO> responseEntityBody = null;
|
|
|
+ try {
|
|
|
+ responseEntityBody = objectMapper.readValue(responseEntityJsonBody, parameterizedTypeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ log.error("解析JSON异常", e);
|
|
|
+ }
|
|
|
+ if (responseEntityBody != null) {
|
|
|
+ if (responseEntityBody.getStatusCode().equalsIgnoreCase(ZeroTrustBusinessRespEnum.SUCCESS.getValue())) {
|
|
|
+
|
|
|
+ // 校验令牌签名
|
|
|
+ if (zerotrustConfig.getCheckTokenSign()) {
|
|
|
+ boolean b = checkUserTokenSign(responseEntityJsonBody);
|
|
|
+ if (!b) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return responseEntityBody.getResult();
|
|
|
+ } else {
|
|
|
+ log.error("{} statusCode:{} , message:{}", requestName, responseEntityBody.getStatusCode(), responseEntityBody.getMessage());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("{} 返回 isnull:{}", requestName, JsonUtils.toJSONString(responseEntity));
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ log.error("{} 请求 error :{}", requestName, JsonUtils.toJSONString(responseEntity));
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public AppTokenInfoRespVO getAppTokenInfo(String appTokenId) {
|
|
|
+ Assert.notBlank(appTokenId);
|
|
|
+ String requestName = "获取应用令牌信息";
|
|
|
+
|
|
|
+ ZeroTrustAppTokenInfoReqVO zeroTrustAppTokenInfoReqVO = new ZeroTrustAppTokenInfoReqVO();
|
|
|
+ zeroTrustAppTokenInfoReqVO.setAppTokenId(appTokenId);
|
|
|
+
|
|
|
+ String reqUrl = zerotrustConfig.getAppTokenQueryUrl();
|
|
|
+ log.info("{} 请求 :{}", requestName, JsonUtils.toJSONString(zeroTrustAppTokenInfoReqVO));
|
|
|
+ RequestEntity<ZeroTrustAppTokenInfoReqVO> httpEntity = new RequestEntity<>(zeroTrustAppTokenInfoReqVO, HttpMethod.POST, URI.create(reqUrl));
|
|
|
+
|
|
|
+ TypeReference<ResultRespVO<AppTokenInfoRespVO>> parameterizedTypeReference =
|
|
|
+ new TypeReference<ResultRespVO<AppTokenInfoRespVO>>() {
|
|
|
+ };
|
|
|
+
|
|
|
+ ResponseEntity<String> responseEntity = restTemplate.exchange(httpEntity, String.class);
|
|
|
+ log.info("{}返回参数 :{}", requestName, JsonUtils.toJSONString(responseEntity));
|
|
|
+
|
|
|
+ if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
|
|
+ String responseEntityJsonBody = responseEntity.getBody();
|
|
|
+
|
|
|
+ ResultRespVO<AppTokenInfoRespVO> responseEntityBody = null;
|
|
|
+ try {
|
|
|
+ responseEntityBody = objectMapper.readValue(responseEntityJsonBody, parameterizedTypeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ log.error("解析JSON异常", e);
|
|
|
+ }
|
|
|
+ if (responseEntityBody != null) {
|
|
|
+ if (responseEntityBody.getStatusCode().equalsIgnoreCase(ZeroTrustBusinessRespEnum.SUCCESS.getValue())) {
|
|
|
+
|
|
|
+ // 校验令牌签名
|
|
|
+ if (zerotrustConfig.getCheckTokenSign()) {
|
|
|
+ boolean b = checkAppTokenSign(responseEntityJsonBody);
|
|
|
+ if (!b) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return responseEntityBody.getResult();
|
|
|
+ } else {
|
|
|
+ log.error("{} statusCode:{} , message:{}", requestName, responseEntityBody.getStatusCode(), responseEntityBody.getMessage());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("{} 返回 isnull:{}", requestName, JsonUtils.toJSONString(responseEntity));
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ log.error("{} 请求 error :{}", requestName, JsonUtils.toJSONString(responseEntity));
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验应用令牌签名
|
|
|
+ *
|
|
|
+ * @param appTokenJson 应用令牌信息
|
|
|
+ * @return 是否成功
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ @SneakyThrows(JsonProcessingException.class)
|
|
|
+ public boolean checkAppTokenSign(String appTokenJson) {
|
|
|
+
|
|
|
+ TypeReference<LinkedHashMap<String, Object>> objectTypeReference = new TypeReference<LinkedHashMap<String, Object>>() {
|
|
|
+ };
|
|
|
+ LinkedHashMap<String, Object> body = objectMapper.readValue(appTokenJson, objectTypeReference);
|
|
|
+ LinkedHashMap<String, Object> userTokenInfo = (LinkedHashMap<String, Object>) body.getOrDefault("result", Collections.emptyMap());
|
|
|
+ LinkedHashMap<String, Object> userToken = (LinkedHashMap<String, Object>) userTokenInfo.getOrDefault("userToken", Collections.emptyMap());
|
|
|
+ String userTokenString = generalAppTokenCheckUserTokenString(userToken);
|
|
|
+ log.info("生成后的用户令牌信息:{}", userTokenString);
|
|
|
+ userTokenInfo.put("userToken", userTokenString);
|
|
|
+ return checkAppTokenSign(userTokenInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验用户令牌签名
|
|
|
+ *
|
|
|
+ * @param userTokenJson 用户令牌JSON
|
|
|
+ * @return 状态
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ @SneakyThrows(JsonProcessingException.class)
|
|
|
+ public boolean checkUserTokenSign(String userTokenJson) {
|
|
|
+
|
|
|
+ TypeReference<LinkedHashMap<String, Object>> objectTypeReference = new TypeReference<LinkedHashMap<String, Object>>() {
|
|
|
+ };
|
|
|
+ LinkedHashMap<String, Object> body = objectMapper.readValue(userTokenJson, objectTypeReference);
|
|
|
+ LinkedHashMap<String, Object> userTokenInfo = (LinkedHashMap<String, Object>) body.getOrDefault("result", Collections.emptyMap());
|
|
|
+ return checkUserTokenSign(userTokenInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成应用令牌校验签名的用户令牌字符串
|
|
|
+ *
|
|
|
+ * @param userTokenMap 用户令牌信息
|
|
|
+ * @return 用户令牌字符串
|
|
|
+ */
|
|
|
+ protected String generalAppTokenCheckUserTokenString(LinkedHashMap<String, Object> userTokenMap) {
|
|
|
+ if (CollUtil.isEmpty(userTokenMap)) {
|
|
|
+ throw new IllegalArgumentException();
|
|
|
+ }
|
|
|
+ String symbol = StrUtil.COMMA + " ";
|
|
|
+
|
|
|
+ StringBuilder userTokenStringBuilder = new StringBuilder("{");
|
|
|
+
|
|
|
+ for (String key : userTokenMap.keySet()) {
|
|
|
+ userTokenStringBuilder.append(key).append("=");
|
|
|
+ String value = userTokenMap.getOrDefault(key, "").toString();
|
|
|
+ userTokenStringBuilder.append(value).append(symbol);
|
|
|
+ }
|
|
|
+
|
|
|
+ String string = userTokenStringBuilder.toString();
|
|
|
+ String subLastSymbol = LangUtil.subLastSymbol(string, symbol);
|
|
|
+ return subLastSymbol + "}";
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验用户令牌签名
|
|
|
+ *
|
|
|
+ * @param useTokenInfoMap 用户令牌信息
|
|
|
+ * @return 签名结果
|
|
|
+ */
|
|
|
+ protected boolean checkUserTokenSign(Map<String, Object> useTokenInfoMap) {
|
|
|
+ String userTokenId = useTokenInfoMap.getOrDefault("userTokenId", "").toString();
|
|
|
+ String createTime = useTokenInfoMap.getOrDefault("createTime", "").toString();
|
|
|
+ String expireAt = useTokenInfoMap.getOrDefault("expireAt", "").toString();
|
|
|
+ String pid = useTokenInfoMap.getOrDefault("pid", "").toString();
|
|
|
+ String orgCode = useTokenInfoMap.getOrDefault("orgCode", "").toString();
|
|
|
+ String ip = useTokenInfoMap.getOrDefault("ip", "").toString();
|
|
|
+ String mid = useTokenInfoMap.getOrDefault("mid", "").toString();
|
|
|
+ String env = useTokenInfoMap.getOrDefault("env", "").toString();
|
|
|
+ String sign = useTokenInfoMap.getOrDefault("sign", "").toString();
|
|
|
+
|
|
|
+ String origin = "userTokenId=" + userTokenId +
|
|
|
+ "&createTime=" + createTime + "&expireAt=" + expireAt + "&pid=" + pid + "&orgCode=" + orgCode
|
|
|
+ + "&ip=" + ip + "&mid=" + mid + "&env=" + env;
|
|
|
+ log.info("用户令牌校验签名签名原文:{}", origin);
|
|
|
+
|
|
|
+ String generalSign = smFactory.getSM3().summary(origin).toString().toLowerCase(Locale.ROOT);
|
|
|
+ log.info("用户令牌校验签名签名后的值:{}", generalSign);
|
|
|
+ log.info("用户令牌校验签名令牌信息中的签名值:{}", sign);
|
|
|
+
|
|
|
+ boolean ignoreCase = generalSign.equalsIgnoreCase(sign);
|
|
|
+ log.info("用户令牌:{} 校验签名签名结果:{}", userTokenId, ignoreCase);
|
|
|
+
|
|
|
+ return ignoreCase;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验应用令牌签名
|
|
|
+ *
|
|
|
+ * @param apTokenInfoMap 应用令牌信息
|
|
|
+ * @return 签名结果
|
|
|
+ */
|
|
|
+ protected boolean checkAppTokenSign(Map<String, Object> apTokenInfoMap) {
|
|
|
+ String appTokenId = apTokenInfoMap.getOrDefault("appTokenId", "").toString();
|
|
|
+ String createTime = apTokenInfoMap.getOrDefault("createTime", "").toString();
|
|
|
+ String expireAt = apTokenInfoMap.getOrDefault("expireAt", "").toString();
|
|
|
+ String appId = apTokenInfoMap.getOrDefault("appId", "").toString();
|
|
|
+ String userToken = apTokenInfoMap.getOrDefault("userToken", "").toString();
|
|
|
+ String sign = apTokenInfoMap.getOrDefault("sign", "").toString();
|
|
|
+
|
|
|
+ String origin = "appTokenId=" + appTokenId + "&createTime=" + createTime + "&expireAt=" +
|
|
|
+ expireAt + "&appId=" + appId + "&userToken=" + userToken;
|
|
|
+ log.info("应用令牌校验签名签名原文:{}", origin);
|
|
|
+
|
|
|
+ String generalSign = smFactory.getSM3().summary(origin).toString().toLowerCase(Locale.ROOT);
|
|
|
+ log.info("应用令牌校验签名签名后的值:{}", generalSign);
|
|
|
+ log.info("应用令牌校验签名令牌信息中的签名值:{}", sign);
|
|
|
+
|
|
|
+ boolean ignoreCase = generalSign.equalsIgnoreCase(sign);
|
|
|
+ log.info("应用令牌:{} 校验签名签名结果:{}", appTokenId, ignoreCase);
|
|
|
+
|
|
|
+ return ignoreCase;
|
|
|
+ }
|
|
|
+}
|