Przeglądaj źródła

1、增加签名错误问题的记录(排查问题)

rayson 6 miesięcy temu
rodzic
commit
043e1c365e

+ 6 - 0
citu-framework/citu-spring-boot-starter-protection/pom.xml

@@ -42,6 +42,12 @@
             <artifactId>citu-spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>com.citu</groupId>
+            <artifactId>menduner-system-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 3 - 2
citu-framework/citu-spring-boot-starter-protection/src/main/java/com/citu/framework/signature/config/CituApiSignatureAutoConfiguration.java

@@ -3,6 +3,7 @@ package com.citu.framework.signature.config;
 import com.citu.framework.redis.config.CituRedisAutoConfiguration;
 import com.citu.framework.signature.core.aop.ApiSignatureAspect;
 import com.citu.framework.signature.core.redis.ApiSignatureRedisDAO;
+import com.citu.module.menduner.system.api.error.ErrorRecordApi;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.data.redis.core.StringRedisTemplate;
@@ -16,8 +17,8 @@ import org.springframework.data.redis.core.StringRedisTemplate;
 public class CituApiSignatureAutoConfiguration {
 
     @Bean
-    public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {
-        return new ApiSignatureAspect(signatureRedisDAO);
+    public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO, ErrorRecordApi errorRecordApi) {
+        return new ApiSignatureAspect(signatureRedisDAO,errorRecordApi);
     }
 
     @Bean

+ 38 - 2
citu-framework/citu-spring-boot-starter-protection/src/main/java/com/citu/framework/signature/core/aop/ApiSignatureAspect.java

@@ -6,10 +6,13 @@ import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.DigestUtil;
+import com.alibaba.fastjson.JSON;
 import com.citu.framework.common.exception.ServiceException;
 import com.citu.framework.common.util.servlet.ServletUtils;
 import com.citu.framework.signature.core.annotation.ApiSignature;
 import com.citu.framework.signature.core.redis.ApiSignatureRedisDAO;
+import com.citu.module.menduner.system.api.error.ErrorRecordApi;
+import com.citu.module.menduner.system.api.error.ErrorRecordReqDTO;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
@@ -34,6 +37,11 @@ public class ApiSignatureAspect {
 
     private final ApiSignatureRedisDAO signatureRedisDAO;
 
+    private final ErrorRecordApi errorRecordApi;
+
+    private static final ThreadLocal<Map<String,Object>> recordThreadLocal = new ThreadLocal<>();
+
+
     /**
      * 获取请求头加签参数 Map
      *
@@ -56,7 +64,7 @@ public class ApiSignatureAspect {
      * @return queryParams
      */
     private static SortedMap<String, String> getRequestParameterMap(HttpServletRequest request) {
-         SortedMap<String, String> sortedMap = new TreeMap<>();
+        SortedMap<String, String> sortedMap = new TreeMap<>();
         for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
             sortedMap.put(entry.getKey(), entry.getValue()[0]);
         }
@@ -74,9 +82,16 @@ public class ApiSignatureAspect {
     @Before("@annotation(signature)")
     public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {
         // 1. 验证通过,直接结束
+        recordThreadLocal.set(new HashMap<>());
         if (verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {
+            recordThreadLocal.remove();
             return;
         }
+        errorRecordApi.create(ErrorRecordReqDTO.builder()
+                .mark(ServletUtils.getRequest().getHeader(signature.timestamp()))
+                .content(JSON.toJSONString(recordThreadLocal.get()))
+                .build());
+        recordThreadLocal.remove();
 
         // 2. 验证不通过,抛出异常
         log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
@@ -93,13 +108,18 @@ public class ApiSignatureAspect {
         // 1.2 校验 appId 是否能获取到对应的 appSecret
         String appId = request.getHeader(signature.appId());
         String appSecret = signatureRedisDAO.getAppSecret(appId);
+        recordThreadLocal.get().put("appSecret", appSecret);
         Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId);
 
         // 2. 校验签名【重要!】
         String clientSignature = request.getHeader(signature.sign()); // 客户端签名
         String serverSignatureString = buildSignatureString(signature, request, appSecret); // 服务端签名字符串
         String serverSignature = DigestUtil.sha256Hex(serverSignatureString); // 服务端签名
+        recordThreadLocal.get().put("clientSignature", clientSignature);
+        recordThreadLocal.get().put("serverSignatureString", serverSignatureString);
+        recordThreadLocal.get().put("serverSignature", serverSignature);
         if (ObjUtil.notEqual(clientSignature, serverSignature)) {
+            recordThreadLocal.get().put("reason", "ObjUtil.notEqual(clientSignature, serverSignature)");
             return false;
         }
 
@@ -124,19 +144,27 @@ public class ApiSignatureAspect {
     private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) {
         // 1. 非空校验
         String appId = request.getHeader(signature.appId());
+        recordThreadLocal.get().put("appId", appId);
         if (StrUtil.isBlank(appId)) {
+            recordThreadLocal.get().put("reason", "appId is null");
             return false;
         }
         String timestamp = request.getHeader(signature.timestamp());
+        recordThreadLocal.get().put("timestamp", timestamp);
         if (StrUtil.isBlank(timestamp)) {
+            recordThreadLocal.get().put("reason", "timestamp is null");
             return false;
         }
         String nonce = request.getHeader(signature.nonce());
+        recordThreadLocal.get().put("nonce", nonce);
         if (StrUtil.length(nonce) < 10) {
+            recordThreadLocal.get().put("reason", "timestamp length < 10");
             return false;
         }
         String sign = request.getHeader(signature.sign());
+        recordThreadLocal.get().put("sign", sign);
         if (StrUtil.isBlank(sign)) {
+            recordThreadLocal.get().put("reason", "sign is null");
             return false;
         }
 
@@ -144,12 +172,20 @@ public class ApiSignatureAspect {
         long expireTime = signature.timeUnit().toMillis(signature.timeout());
         long requestTimestamp = Long.parseLong(timestamp);
         long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
+        recordThreadLocal.get().put("expireTime", expireTime);
+        recordThreadLocal.get().put("requestTimestamp", requestTimestamp);
+        recordThreadLocal.get().put("timestampDisparity", timestampDisparity);
         if (timestampDisparity > expireTime) {
+            recordThreadLocal.get().put("reason", "timestampDisparity > expireTime 超出范围");
             return false;
         }
 
         // 3. 检查 nonce 是否存在,有且仅能使用一次
-        return signatureRedisDAO.getNonce(appId, nonce) == null;
+        boolean result = signatureRedisDAO.getNonce(appId, nonce) == null;
+        if(!result) {
+            recordThreadLocal.get().put("reason", "signatureRedisDAO.getNonce(appId, nonce) != null");
+        }
+        return result;
     }
 
     /**

+ 21 - 0
menduner/menduner-system-api/src/main/java/com/citu/module/menduner/system/api/error/ErrorRecordApi.java

@@ -0,0 +1,21 @@
+package com.citu.module.menduner.system.api.error;
+
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.module.menduner.system.enums.ApiConstants;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+@FeignClient(name = ApiConstants.NAME)
+@Tag(name = "RPC 服务 - 错误Api信息")
+public interface ErrorRecordApi {
+
+    String PREFIX = ApiConstants.PREFIX + "/error-record";
+
+
+    @PostMapping(PREFIX + "/create")
+    @Operation(summary = "创建错误日志")
+    CommonResult<Boolean> create(@RequestBody ErrorRecordReqDTO req);
+}

+ 18 - 0
menduner/menduner-system-api/src/main/java/com/citu/module/menduner/system/api/error/ErrorRecordReqDTO.java

@@ -0,0 +1,18 @@
+package com.citu.module.menduner.system.api.error;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+@Schema(description = "错误记录新增/修改 Request VO")
+@Data
+@Builder
+public class ErrorRecordReqDTO {
+
+    @Schema(description = "唯一标识")
+    private String mark;
+
+    @Schema(description = "内容")
+    private String content;
+
+}

+ 30 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/api/error/ErrorRecordApiImpl.java

@@ -0,0 +1,30 @@
+package com.citu.module.menduner.system.api.error;
+
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.module.menduner.common.util.LoginUserContext;
+import com.citu.module.menduner.system.dal.dataobject.error.ErrorRecordDO;
+import com.citu.module.menduner.system.dal.mysql.error.ErrorRecordMapper;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.pojo.CommonResult.success;
+
+@RestController // 提供 RESTful API 接口,给 Feign 调用
+@Validated
+public class ErrorRecordApiImpl implements ErrorRecordApi{
+
+    @Resource
+    private ErrorRecordMapper errorRecordMapper;
+
+    @Override
+    public CommonResult<Boolean> create(ErrorRecordReqDTO req) {
+        ErrorRecordDO insert = BeanUtils.toBean(req, ErrorRecordDO.class);
+        insert.setUserId(LoginUserContext.getUserId2());
+        insert.setEnterpriseId(LoginUserContext.getEnterpriseId3());
+        errorRecordMapper.insert(insert);
+        return success(true);
+    }
+}

+ 3 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/error/ErrorRecordSaveReqVO.java

@@ -7,6 +7,9 @@ import lombok.Data;
 @Data
 public class ErrorRecordSaveReqVO {
 
+    @Schema(description = "唯一标识")
+    private String mark;
+
     @Schema(description = "内容")
     private String content;
 

+ 4 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/error/ErrorRecordDO.java

@@ -35,6 +35,10 @@ public class ErrorRecordDO extends TenantBaseDO {
      * 企业id
      */
     private Long enterpriseId;
+    /**
+     * 标记
+     */
+    private String mark;
     /**
      * 内容
      */