Prechádzať zdrojové kódy

171 fix(protection): 修复HTTP接口签名 API重复请求问题

YunaiV 4 mesiacov pred
rodič
commit
f6601ab639

+ 10 - 4
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java

@@ -2,10 +2,12 @@ package cn.iocoder.yudao.framework.signature.core.aop;
 
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.BooleanUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.DigestUtil;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
 import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
@@ -69,13 +71,17 @@ public class ApiSignatureAspect {
 
         // 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 )
         String nonce = request.getHeader(signature.nonce());
-        signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit());
+        if (BooleanUtil.isFalse(signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit()))) {
+            String timestamp = request.getHeader(signature.timestamp());
+            log.info("[verifySignature][appId({}) timestamp({}) nonce({}) sign({}) 存在重复请求]", appId, timestamp, nonce, clientSignature);
+            throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), "存在重复请求");
+        }
         return true;
     }
 
     /**
      * 校验请求头加签参数
-     *
+     * <p>
      * 1. appId 是否为空
      * 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
      * 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
@@ -118,7 +124,7 @@ public class ApiSignatureAspect {
 
     /**
      * 构建签名字符串
-     *
+     * <p>
      * 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
      *
      * @param signature signature
@@ -139,7 +145,7 @@ public class ApiSignatureAspect {
     /**
      * 获取请求头加签参数 Map
      *
-     * @param request 请求
+     * @param request   请求
      * @param signature 签名注解
      * @return signature params
      */

+ 4 - 4
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java

@@ -17,7 +17,7 @@ public class ApiSignatureRedisDAO {
 
     /**
      * 验签随机数
-     *
+     * <p>
      * KEY 格式:signature_nonce:%s // 参数为 随机数
      * VALUE 格式:String
      * 过期时间:不固定
@@ -26,7 +26,7 @@ public class ApiSignatureRedisDAO {
 
     /**
      * 签名密钥
-     *
+     * <p>
      * HASH 结构
      * KEY 格式:%s // 参数为 appid
      * VALUE 格式:String
@@ -40,8 +40,8 @@ public class ApiSignatureRedisDAO {
         return stringRedisTemplate.opsForValue().get(formatNonceKey(appId, nonce));
     }
 
-    public void setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
-        stringRedisTemplate.opsForValue().set(formatNonceKey(appId, nonce), "", time, timeUnit);
+    public Boolean setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
+        return stringRedisTemplate.opsForValue().setIfAbsent(formatNonceKey(appId, nonce), "", time, timeUnit);
     }
 
     private static String formatNonceKey(String appId, String nonce) {