Commit 5f278ac23b392d6ecd9452cc50fdfc9e77af97e8
1 parent
5bc01901
【新增】优化部分代码风格
Showing
5 changed files
with
26 additions
and
43 deletions
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/Signature.java renamed to yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/annotation/ApiSignature.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.signature.core.annotation; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 4 | 4 | |
| 5 | 5 | import java.lang.annotation.*; |
| 6 | +import java.util.concurrent.TimeUnit; | |
| 6 | 7 | |
| 7 | 8 | |
| 8 | 9 | /** |
| ... | ... | @@ -14,12 +15,17 @@ import java.lang.annotation.*; |
| 14 | 15 | @Documented |
| 15 | 16 | @Target({ElementType.METHOD, ElementType.TYPE}) |
| 16 | 17 | @Retention(RetentionPolicy.RUNTIME) |
| 17 | -public @interface Signature { | |
| 18 | +public @interface ApiSignature { | |
| 18 | 19 | |
| 19 | 20 | /** |
| 20 | - * 同一个请求多长时间内有效 默认 10分钟 | |
| 21 | + * 同一个请求多长时间内有效 默认 60 秒 | |
| 21 | 22 | */ |
| 22 | - long expireTime() default 600000L; | |
| 23 | + int timeout() default 60; | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 时间单位,默认为 SECONDS 秒 | |
| 27 | + */ | |
| 28 | + TimeUnit timeUnit() default TimeUnit.SECONDS; | |
| 23 | 29 | |
| 24 | 30 | // ========================== 签名参数 ========================== |
| 25 | 31 | |
| ... | ... | @@ -50,8 +56,4 @@ public @interface Signature { |
| 50 | 56 | */ |
| 51 | 57 | String sign() default "sign"; |
| 52 | 58 | |
| 53 | - /** | |
| 54 | - * url 客户端不需要传递,但是可以用来加签(如: /{id} 带有动态参数的 url ,如果没有动态参数可设置为 false 不进行加签) | |
| 55 | - */ | |
| 56 | - boolean urlEnable() default true; | |
| 57 | 59 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/aop/SignatureAspect.java
| 1 | 1 | package cn.iocoder.yudao.framework.signature.core.aop; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.hutool.crypto.SignUtil; |
| 6 | 7 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 9 | -import cn.iocoder.yudao.framework.signature.core.annotation.Signature; | |
| 10 | +import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature; | |
| 10 | 11 | import cn.iocoder.yudao.framework.signature.core.redis.SignatureRedisDAO; |
| 11 | 12 | import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyWrapper; |
| 12 | 13 | import jakarta.servlet.http.HttpServletRequest; |
| ... | ... | @@ -15,7 +16,6 @@ import lombok.extern.slf4j.Slf4j; |
| 15 | 16 | import org.aspectj.lang.JoinPoint; |
| 16 | 17 | import org.aspectj.lang.annotation.Aspect; |
| 17 | 18 | import org.aspectj.lang.annotation.Before; |
| 18 | -import org.springframework.util.Assert; | |
| 19 | 19 | |
| 20 | 20 | import java.nio.charset.StandardCharsets; |
| 21 | 21 | import java.util.Map; |
| ... | ... | @@ -25,7 +25,7 @@ import java.util.TreeMap; |
| 25 | 25 | import java.util.concurrent.TimeUnit; |
| 26 | 26 | |
| 27 | 27 | /** |
| 28 | - * 拦截声明了 {@link Signature} 注解的方法,实现签名 | |
| 28 | + * 拦截声明了 {@link ApiSignature} 注解的方法,实现签名 | |
| 29 | 29 | * |
| 30 | 30 | * @author Zhougang |
| 31 | 31 | */ |
| ... | ... | @@ -37,9 +37,9 @@ public class SignatureAspect { |
| 37 | 37 | private final SignatureRedisDAO signatureRedisDAO; |
| 38 | 38 | |
| 39 | 39 | @Before("@annotation(signature)") |
| 40 | - public void beforePointCut(JoinPoint joinPoint, Signature signature) { | |
| 40 | + public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) { | |
| 41 | 41 | if (!verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) { |
| 42 | - log.info("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(), | |
| 42 | + log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(), | |
| 43 | 43 | joinPoint.getArgs()); |
| 44 | 44 | String message = StrUtil.blankToDefault(signature.message(), |
| 45 | 45 | GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); |
| ... | ... | @@ -47,25 +47,22 @@ public class SignatureAspect { |
| 47 | 47 | } |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | - private boolean verifySignature(Signature signature, HttpServletRequest request) { | |
| 50 | + private boolean verifySignature(ApiSignature signature, HttpServletRequest request) { | |
| 51 | 51 | if (!verifyHeaders(signature, request)) { |
| 52 | 52 | return false; |
| 53 | 53 | } |
| 54 | 54 | // 校验 appId 是否能获取到对应的 appSecret |
| 55 | 55 | String appId = request.getHeader(signature.appId()); |
| 56 | 56 | String appSecret = signatureRedisDAO.getAppSecret(appId); |
| 57 | - Assert.notNull(appSecret, "找不到对应的 appSecret"); | |
| 57 | + Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId); | |
| 58 | 58 | // 请求头 |
| 59 | 59 | SortedMap<String, String> headersMap = getRequestHeaders(signature, request); |
| 60 | - // 如:/user/{id} url 带有动态参数的情况 | |
| 61 | - String urlParams = signature.urlEnable() ? request.getServletPath() : ""; | |
| 62 | 60 | // 请求参数 |
| 63 | 61 | String requestParams = getRequestParams(request); |
| 64 | 62 | // 请求体 |
| 65 | - String requestBody = getRequestBody(request); | |
| 63 | + String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : ""; | |
| 66 | 64 | // 生成服务端签名 |
| 67 | - String serverSignature = SignUtil.signParamsSha256(headersMap, | |
| 68 | - urlParams + requestParams + requestBody + appSecret); | |
| 65 | + String serverSignature = SignUtil.signParamsSha256(headersMap, requestParams + requestBody + appSecret); | |
| 69 | 66 | // 客户端签名 |
| 70 | 67 | String clientSignature = request.getHeader(signature.sign()); |
| 71 | 68 | if (!StrUtil.equals(clientSignature, serverSignature)) { |
| ... | ... | @@ -73,7 +70,7 @@ public class SignatureAspect { |
| 73 | 70 | } |
| 74 | 71 | String nonce = headersMap.get(signature.nonce()); |
| 75 | 72 | // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) |
| 76 | - signatureRedisDAO.setNonce(nonce, signature.expireTime(), TimeUnit.MILLISECONDS); | |
| 73 | + signatureRedisDAO.setNonce(nonce, signature.timeout() * 2L, signature.timeUnit()); | |
| 77 | 74 | return true; |
| 78 | 75 | } |
| 79 | 76 | |
| ... | ... | @@ -87,7 +84,7 @@ public class SignatureAspect { |
| 87 | 84 | * @param signature signature |
| 88 | 85 | * @param request request |
| 89 | 86 | */ |
| 90 | - private boolean verifyHeaders(Signature signature, HttpServletRequest request) { | |
| 87 | + private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) { | |
| 91 | 88 | String appId = request.getHeader(signature.appId()); |
| 92 | 89 | if (StrUtil.isBlank(appId)) { |
| 93 | 90 | return false; |
| ... | ... | @@ -97,7 +94,7 @@ public class SignatureAspect { |
| 97 | 94 | return false; |
| 98 | 95 | } |
| 99 | 96 | String nonce = request.getHeader(signature.nonce()); |
| 100 | - if (StrUtil.isBlank(nonce) || nonce.length() < 10) { | |
| 97 | + if (StrUtil.isBlank(nonce) || StrUtil.length(nonce) < 10) { | |
| 101 | 98 | return false; |
| 102 | 99 | } |
| 103 | 100 | String sign = request.getHeader(signature.sign()); |
| ... | ... | @@ -105,7 +102,7 @@ public class SignatureAspect { |
| 105 | 102 | return false; |
| 106 | 103 | } |
| 107 | 104 | // 其他合法性校验 |
| 108 | - long expireTime = signature.expireTime(); | |
| 105 | + long expireTime = signature.timeUnit().toMillis(signature.timeout()); | |
| 109 | 106 | long requestTimestamp = Long.parseLong(timestamp); |
| 110 | 107 | // 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值) |
| 111 | 108 | long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp); |
| ... | ... | @@ -122,7 +119,7 @@ public class SignatureAspect { |
| 122 | 119 | * @param request request |
| 123 | 120 | * @return signature params |
| 124 | 121 | */ |
| 125 | - private SortedMap<String, String> getRequestHeaders(Signature signature, HttpServletRequest request) { | |
| 122 | + private SortedMap<String, String> getRequestHeaders(ApiSignature signature, HttpServletRequest request) { | |
| 126 | 123 | SortedMap<String, String> sortedMap = new TreeMap<>(); |
| 127 | 124 | sortedMap.put(signature.appId(), request.getHeader(signature.appId())); |
| 128 | 125 | sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp())); |
| ... | ... | @@ -154,17 +151,5 @@ public class SignatureAspect { |
| 154 | 151 | return queryString.substring(1); |
| 155 | 152 | } |
| 156 | 153 | |
| 157 | - /** | |
| 158 | - * 获取请求体参数 | |
| 159 | - * | |
| 160 | - * @param request request | |
| 161 | - * @return body | |
| 162 | - */ | |
| 163 | - private String getRequestBody(HttpServletRequest request) { | |
| 164 | - CacheRequestBodyWrapper requestWrapper = new CacheRequestBodyWrapper(request); | |
| 165 | - // 获取 body | |
| 166 | - return new String(requestWrapper.getBody(), StandardCharsets.UTF_8); | |
| 167 | - } | |
| 168 | - | |
| 169 | 154 | } |
| 170 | 155 | ... | ... |
yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/signature/core/redis/SignatureRedisDAO.java
| ... | ... | @@ -42,8 +42,7 @@ public class SignatureRedisDAO { |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 44 | public void setNonce(String nonce, long time, TimeUnit timeUnit) { |
| 45 | - // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) | |
| 46 | - stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), nonce, time * 2, timeUnit); | |
| 45 | + stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), nonce, time, timeUnit); | |
| 47 | 46 | } |
| 48 | 47 | |
| 49 | 48 | private static String formatAppIdKey(String key) { | ... | ... |
yudao-framework/yudao-spring-boot-starter-protection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
| 1 | 1 | cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration |
| 2 | 2 | cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration |
| 3 | -cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration | |
| 4 | 3 | \ No newline at end of file |
| 4 | +cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration | |
| 5 | +cn.iocoder.yudao.framework.signature.config.YudaoSignatureAutoConfiguration | |
| 5 | 6 | \ No newline at end of file | ... | ... |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyWrapper.java
| ... | ... | @@ -23,10 +23,6 @@ public class CacheRequestBodyWrapper extends HttpServletRequestWrapper { |
| 23 | 23 | */ |
| 24 | 24 | private final byte[] body; |
| 25 | 25 | |
| 26 | - public byte[] getBody() { | |
| 27 | - return body; | |
| 28 | - } | |
| 29 | - | |
| 30 | 26 | public CacheRequestBodyWrapper(HttpServletRequest request) { |
| 31 | 27 | super(request); |
| 32 | 28 | body = ServletUtils.getBodyBytes(request); | ... | ... |