diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java index c1c78ac57b..e568346aa3 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/ApiSignatureAspect.java +++ b/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; } /** * 校验请求头加签参数 - * + *

* 1. appId 是否为空 * 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟 * 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了 @@ -118,7 +124,7 @@ public class ApiSignatureAspect { /** * 构建签名字符串 - * + *

* 格式为 = 请求参数 + 请求体 + 请求头 + 密钥 * * @param signature signature @@ -139,7 +145,7 @@ public class ApiSignatureAspect { /** * 获取请求头加签参数 Map * - * @param request 请求 + * @param request 请求 * @param signature 签名注解 * @return signature params */ diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java index 11fe384dac..7f3b119d53 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/ApiSignatureRedisDAO.java +++ b/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 { /** * 验签随机数 - * + *

* KEY 格式:signature_nonce:%s // 参数为 随机数 * VALUE 格式:String * 过期时间:不固定 @@ -26,7 +26,7 @@ public class ApiSignatureRedisDAO { /** * 签名密钥 - * + *

* 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) {