Bläddra i källkod

Merge branch 'release/v1.3.1' into 'master'

Release/v1.3.1

See merge request dcuc-tjdsj/approve-core!101
黄资权 1 år sedan
förälder
incheckning
99fb1e9b3e

+ 49 - 0
approve-api/src/main/java/com/dragonsoft/dcuc/approve/enumresources/ZeroTrustTokenActionEnum.java

@@ -0,0 +1,49 @@
+package com.dragonsoft.dcuc.approve.enumresources;
+
+import com.dragonsoft.duceap.base.enums.ICodeEnum;
+
+/**
+ * <p>
+ * 令牌动作
+ * </p>
+ *
+ * @author huangzqa
+ * @date 2022/8/1
+ */
+public enum ZeroTrustTokenActionEnum implements ICodeEnum {
+    /**
+     * 上线
+     */
+    ONLINE("online", "上线"),
+
+    /**
+     * 下线
+     */
+    OFFLINE("offline", "下线"),
+
+    /**
+     * 续期
+     */
+    RENEW("renew", "续期"),
+
+    ;
+    private final String value;
+
+    private final String label;
+
+
+    ZeroTrustTokenActionEnum(String value, String label) {
+        this.value = value;
+        this.label = label;
+    }
+
+    @Override
+    public String getValue() {
+        return this.value;
+    }
+
+    @Override
+    public String getLabel() {
+        return this.label;
+    }
+}

+ 37 - 0
approve-api/src/main/java/com/dragonsoft/dcuc/approve/enumresources/ZeroTrustTokenTypeEnum.java

@@ -0,0 +1,37 @@
+package com.dragonsoft.dcuc.approve.enumresources;
+
+import com.dragonsoft.duceap.base.enums.ICodeEnum;
+
+/**
+ * @author huangzqa
+ */
+
+public enum ZeroTrustTokenTypeEnum implements ICodeEnum {
+    /**
+     * 用户令牌
+     */
+    USER("user", "用户令牌"),
+    /**
+     * 应用令牌
+     */
+    APP("app", "应用令牌");
+
+    private final String value;
+
+    private final String label;
+
+    ZeroTrustTokenTypeEnum(String value, String label) {
+        this.value = value;
+        this.label = label;
+    }
+
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String getLabel() {
+        return label;
+    }
+}

+ 24 - 0
approve-api/src/main/java/com/dragonsoft/dcuc/approve/facade/api/IApiV1TokenFacade.java

@@ -0,0 +1,24 @@
+package com.dragonsoft.dcuc.approve.facade.api;
+
+import com.dragonsoft.dcuc.approve.model.resp.v3.ZeroTrustMessageRespVO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+/**
+ * @author mazq
+ * @date 2023/2/22
+ */
+@FeignClient(name = "approve", path = "api/v1/tokens/")
+public interface IApiV1TokenFacade {
+
+    /**
+     * 接收令牌
+     *
+     * @param tokenReceiveJson
+     * @return
+     */
+    @PostMapping("receive")
+    ZeroTrustMessageRespVO tokenReceive(@RequestBody String tokenReceiveJson);
+
+}

+ 35 - 0
approve-api/src/main/java/com/dragonsoft/dcuc/approve/model/req/v1/token/TokenReceiveVO.java

@@ -0,0 +1,35 @@
+package com.dragonsoft.dcuc.approve.model.req.v1.token;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author mazq
+ * @date 2023/3/31
+ */
+@Data
+public class TokenReceiveVO {
+
+    @NotBlank
+    @ApiModelProperty(value = "令牌行为 online 上线 offline 下线 renew续期")
+    private String action;
+
+    @NotBlank
+    @ApiModelProperty(value = "令牌类型 user用户令牌 app应用令牌")
+    private String type;
+
+    @NotBlank
+    @ApiModelProperty(value = "令牌详细信息")
+    private String token;
+
+    @NotBlank
+    @ApiModelProperty(value = "签名随机数")
+    private String nonce;
+
+    @NotBlank
+    @ApiModelProperty(value = "签名")
+    private String sign;
+
+}

+ 18 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/business/ICacheBusiness.java

@@ -1,5 +1,8 @@
 package com.dragonsoft.dcuc.approve.business;
 
+import com.dragonsoft.dcuc.approve.dto.zerotrust.AppTokenInfoDTO;
+import com.dragonsoft.dcuc.approve.dto.zerotrust.UserTokenInfoDTO;
+
 import java.util.Date;
 
 /**
@@ -39,4 +42,19 @@ public interface ICacheBusiness {
      */
     Date getLastUserSyncTime();
 
+    /**
+     * 接收用户令牌处理
+     *
+     * @param tokenInfo
+     * @param action
+     */
+    void appTokenActionHandle(AppTokenInfoDTO tokenInfo, String action);
+
+    /**
+     * 接收应用令牌处理
+     *
+     * @param tokenInfo
+     * @param action
+     */
+    void userTokenActionHandle(UserTokenInfoDTO tokenInfo, String action);
 }

+ 20 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/business/IZeroTrustTokenBusiness.java

@@ -0,0 +1,20 @@
+package com.dragonsoft.dcuc.approve.business;
+
+import com.dragonsoft.dcuc.approve.model.req.v1.token.TokenReceiveVO;
+import com.dragonsoft.dcuc.approve.model.resp.v3.ZeroTrustMessageRespVO;
+
+/**
+ * @author mazq
+ * @date 2023/2/14
+ */
+public interface IZeroTrustTokenBusiness {
+
+    /**
+     * 接收令牌
+     *
+     * @param receiveVo
+     * @return
+     */
+    ZeroTrustMessageRespVO tokenReceive(TokenReceiveVO receiveVo);
+
+}

+ 73 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/business/impl/RedisCacheBusinessImpl.java

@@ -1,8 +1,12 @@
 package com.dragonsoft.dcuc.approve.business.impl;
 
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
 import com.dragonsoft.dcuc.approve.business.ICacheBusiness;
 import com.dragonsoft.dcuc.approve.constants.ApproveRedisConstants;
+import com.dragonsoft.dcuc.approve.dto.zerotrust.AppTokenInfoDTO;
+import com.dragonsoft.dcuc.approve.dto.zerotrust.UserTokenInfoDTO;
+import com.dragonsoft.dcuc.approve.enumresources.ZeroTrustTokenActionEnum;
 import com.dragonsoft.duceap.commons.util.date.DateConst;
 import com.dragonsoft.duceap.commons.util.date.DateUtils;
 import lombok.extern.slf4j.Slf4j;
@@ -12,6 +16,7 @@ import org.springframework.stereotype.Component;
 
 import java.text.ParseException;
 import java.util.Date;
+import java.util.concurrent.TimeUnit;
 
 /**
  * <p>
@@ -71,4 +76,72 @@ public class RedisCacheBusinessImpl implements ICacheBusiness {
             return null;
         }
     }
+
+
+    @Override
+    public void appTokenActionHandle(AppTokenInfoDTO tokenInfo, String action) {
+        if (null == tokenInfo) {
+            return;
+        }
+        if (ZeroTrustTokenActionEnum.OFFLINE.getValue().equals(action)) {
+            deleteAppToken(tokenInfo);
+        } else if (ZeroTrustTokenActionEnum.ONLINE.getValue().equals(action)) {
+            cacheAppToken(tokenInfo);
+        } else if (ZeroTrustTokenActionEnum.RENEW.getValue().equals(action)) {
+            renewAppToken(tokenInfo);
+        }
+    }
+
+    private void cacheAppToken(AppTokenInfoDTO appToken) {
+        log.info("缓存的应用令牌 令牌id:{}, appId:{}", appToken.getAppTokenId(), appToken.getAppId());
+
+        long tokenExpireTime = appToken.getExpireAt().getTime();
+        long redisExpire = (tokenExpireTime - System.currentTimeMillis()) / 1000;
+        stringRedisTemplate.opsForValue().set(appToken.getAppTokenId(), JSON.toJSONString(appToken), redisExpire, TimeUnit.SECONDS);
+    }
+
+    private void renewAppToken(AppTokenInfoDTO tokenInfo) {
+        log.info("续期应用令牌");
+        cacheAppToken(tokenInfo);
+    }
+
+    private void deleteAppToken(AppTokenInfoDTO tokenInfo) {
+        log.info("删除缓存的应用令牌 令牌id:{}, appId:{}", tokenInfo.getAppTokenId(), tokenInfo.getAppId());
+        stringRedisTemplate.delete(tokenInfo.getAppTokenId());
+    }
+
+    @Override
+    public void userTokenActionHandle(UserTokenInfoDTO tokenInfo, String action) {
+        if (null == tokenInfo) {
+            return;
+        }
+        if (ZeroTrustTokenActionEnum.OFFLINE.getValue().equals(action)) {
+            deleteUserToken(tokenInfo);
+        } else if (ZeroTrustTokenActionEnum.ONLINE.getValue().equals(action)) {
+            cacheUserToken(tokenInfo);
+        } else if (ZeroTrustTokenActionEnum.RENEW.getValue().equals(action)) {
+            renewUserToken(tokenInfo);
+        }
+    }
+
+    private void renewUserToken(UserTokenInfoDTO tokenInfo) {
+        log.info("续期用户令牌");
+        cacheUserToken(tokenInfo);
+    }
+
+    private void cacheUserToken(UserTokenInfoDTO tokenInfo) {
+        log.info("缓存用户令牌 令牌id:{}, pid:{}", tokenInfo.getUserTokenId(), tokenInfo.getPid());
+        // 缓存人员令牌
+        long tokenExpireTime = tokenInfo.getExpireAt().getTime();
+        long redisExpire = (tokenExpireTime - System.currentTimeMillis()) / 1000;
+        stringRedisTemplate.opsForValue().set(tokenInfo.getUserTokenId(), JSON.toJSONString(tokenInfo), redisExpire, TimeUnit.SECONDS);
+
+    }
+
+    private void deleteUserToken(UserTokenInfoDTO tokenInfo) {
+        log.info("删除缓存的用户令牌 令牌id:{}, pid:{}", tokenInfo.getUserTokenId(), tokenInfo.getPid());
+        // 删除令牌
+        stringRedisTemplate.delete(tokenInfo.getUserTokenId());
+    }
+
 }

+ 97 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/business/impl/ZeroTrustTokenBusinessiImpl.java

@@ -0,0 +1,97 @@
+package com.dragonsoft.dcuc.approve.business.impl;
+
+import com.dragonsoft.dcuc.approve.business.ICacheBusiness;
+import com.dragonsoft.dcuc.approve.business.IZeroTrustTokenBusiness;
+import com.dragonsoft.dcuc.approve.dto.zerotrust.AppTokenInfoDTO;
+import com.dragonsoft.dcuc.approve.dto.zerotrust.UserTokenInfoDTO;
+import com.dragonsoft.dcuc.approve.enumresources.ZeroTrustBusinessRespEnum;
+import com.dragonsoft.dcuc.approve.enumresources.ZeroTrustTokenTypeEnum;
+import com.dragonsoft.dcuc.approve.model.req.v1.token.TokenReceiveVO;
+import com.dragonsoft.dcuc.approve.model.resp.v3.ZeroTrustMessageRespVO;
+import com.dragonsoft.smtools.loader.SMFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author mazq
+ * @date 2023/9/8
+ */
+@Slf4j
+@Service
+public class ZeroTrustTokenBusinessiImpl implements IZeroTrustTokenBusiness {
+
+    private ICacheBusiness cacheBusiness;
+
+    private SMFactory smFactory;
+
+    @Autowired
+    public void setSmFactory(SMFactory smFactory) {
+        this.smFactory = smFactory;
+    }
+
+    @Autowired
+    public void setCacheBusiness(ICacheBusiness cacheBusiness) {
+        this.cacheBusiness = cacheBusiness;
+    }
+
+    @SneakyThrows
+    @Override
+    public ZeroTrustMessageRespVO tokenReceive(TokenReceiveVO receiveVo) {
+        // 校验请求签名
+        ZeroTrustMessageRespVO check = tokenReceiveSignCheck(receiveVo);
+        if (check.isRespFail()) {
+            return check;
+        }
+        // 缓存令牌到redis
+        tokenReceiveCache(receiveVo);
+
+        return ZeroTrustMessageRespVO.messageEnumMessage(ZeroTrustBusinessRespEnum.SUCCESS);
+    }
+
+    private ZeroTrustMessageRespVO tokenReceiveSignCheck(TokenReceiveVO receiveVo) {
+        String generalNoticeSign = generalNoticeSign(receiveVo);
+        String requestSign = receiveVo.getSign();
+        if (!generalNoticeSign.equalsIgnoreCase(requestSign)) {
+            log.info("令牌接收签名校验不通过, 请求中的签名:{}, 原文生成的签名:{}", requestSign, generalNoticeSign);
+            return ZeroTrustMessageRespVO.messageEnumMessage(ZeroTrustBusinessRespEnum.OPERATE_FAIL);
+        } else {
+            return ZeroTrustMessageRespVO.messageEnumMessage(ZeroTrustBusinessRespEnum.SUCCESS);
+        }
+    }
+
+    /**
+     * 生成令牌通知签名
+     *
+     * @param tokenReceiveReqVo 应用通知信息
+     * @return 签名
+     */
+    public String generalNoticeSign(TokenReceiveVO tokenReceiveReqVo) {
+        String action = tokenReceiveReqVo.getAction();
+        String type = tokenReceiveReqVo.getType();
+        String token = tokenReceiveReqVo.getToken();
+        String nonce = tokenReceiveReqVo.getNonce();
+
+        String origin = "action=" + action + "&type=" + type + "&token=" + token + "&nonce=" + nonce;
+        log.info("noticeOrigin :{}", origin);
+        return smFactory.getSM3().summary(origin).toString();
+    }
+
+    private void tokenReceiveCache(TokenReceiveVO receiveVo) throws JsonProcessingException {
+        String action = receiveVo.getAction();
+        String type = receiveVo.getType();
+        String token = receiveVo.getToken();
+        ObjectMapper objectMapper = new ObjectMapper();
+        if (ZeroTrustTokenTypeEnum.USER.getValue().equals(type)) {
+            UserTokenInfoDTO tokenInfo = objectMapper.readValue(token, UserTokenInfoDTO.class);
+            cacheBusiness.userTokenActionHandle(tokenInfo, action);
+        } else {
+            AppTokenInfoDTO tokenInfo = objectMapper.readValue(token, AppTokenInfoDTO.class);
+            cacheBusiness.appTokenActionHandle(tokenInfo, action);
+        }
+    }
+
+}

+ 50 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/dto/zerotrust/AppTokenInfoDTO.java

@@ -0,0 +1,50 @@
+package com.dragonsoft.dcuc.approve.dto.zerotrust;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * 应用令牌校验服务接口
+ * </p>
+ *
+ * @author huangzqa
+ * @date 2022/8/1
+ */
+@Data
+public class AppTokenInfoDTO {
+
+    /**
+     * 应用令牌ID
+     */
+    private String appTokenId;
+
+    /**
+     * 用户令牌创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * 用户令牌到期时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date expireAt;
+
+    /**
+     * 应用标识
+     */
+    private String appId;
+
+    /**
+     * 用户令牌详细信息
+     */
+    private UserTokenInfoDTO userToken;
+
+    /**
+     * 应用令牌签名值
+     */
+    private String sign;
+}

+ 71 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/dto/zerotrust/UserTokenInfoDTO.java

@@ -0,0 +1,71 @@
+package com.dragonsoft.dcuc.approve.dto.zerotrust;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * <p>
+ * 用户令牌校验服务接口
+ * </p>
+ *
+ * @author huangzqa
+ * @date 2022/8/1
+ */
+@Data
+public class UserTokenInfoDTO {
+
+    /**
+     * 用户令牌id
+     */
+    private String userTokenId;
+
+    /**
+     * 用户令牌创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * 用户令牌到期时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date expireAt;
+
+    /**
+     * 用户标识
+     */
+    private String pid;
+
+    /**
+     * 组织机构编码
+     */
+    private String orgCode;
+
+    /**
+     * 终端 IP 地址
+     */
+    private String ip;
+
+    /**
+     * 终端设备标识
+     */
+    private String mid;
+
+    /**
+     * 终端环境类型
+     */
+    private String env;
+
+    /**
+     * 用户名称
+     */
+    private String name;
+
+    /**
+     * 令牌内容签名
+     */
+    private String sign;
+
+}

+ 43 - 0
approve-core-service/src/main/java/com/dragonsoft/dcuc/approve/facade/api/ApiV1TokenFacade.java

@@ -0,0 +1,43 @@
+package com.dragonsoft.dcuc.approve.facade.api;
+
+import com.dragonsoft.dcuc.approve.business.IZeroTrustTokenBusiness;
+import com.dragonsoft.dcuc.approve.enumresources.ZeroTrustBusinessRespEnum;
+import com.dragonsoft.dcuc.approve.model.req.v1.token.TokenReceiveVO;
+import com.dragonsoft.dcuc.approve.model.resp.v3.ZeroTrustMessageRespVO;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author mazq
+ * @date 2023/9/8
+ */
+@Slf4j
+@RestController
+@RequestMapping("api/v1/tokens/")
+public class ApiV1TokenFacade implements IApiV1TokenFacade {
+
+    private IZeroTrustTokenBusiness zeroTrustTokenBusiness;
+
+    @Autowired
+    public void setZeroTrustTokenBusiness(IZeroTrustTokenBusiness zeroTrustTokenBusiness) {
+        this.zeroTrustTokenBusiness = zeroTrustTokenBusiness;
+    }
+
+    @Override
+    public ZeroTrustMessageRespVO tokenReceive(String tokenReceiveJson) {
+        log.info("审批服务接收到令牌操作信息:{}", tokenReceiveJson);
+        ObjectMapper objectMapper = new ObjectMapper();
+        TokenReceiveVO tokenReceiveVO;
+        try {
+            tokenReceiveVO = objectMapper.readValue(tokenReceiveJson, TokenReceiveVO.class);
+        } catch (Exception e) {
+            log.error("解析令牌信息异常", e);
+            return ZeroTrustMessageRespVO.messageEnumMessage(ZeroTrustBusinessRespEnum.OPERATE_FAIL);
+        }
+
+        return zeroTrustTokenBusiness.tokenReceive(tokenReceiveVO);
+    }
+}