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,6 +3,7 @@ package cn.iocoder.yudao.framework.signature.core.annotation; | ||
| 3 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | 3 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 4 | 4 | ||
| 5 | import java.lang.annotation.*; | 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,12 +15,17 @@ import java.lang.annotation.*; | ||
| 14 | @Documented | 15 | @Documented |
| 15 | @Target({ElementType.METHOD, ElementType.TYPE}) | 16 | @Target({ElementType.METHOD, ElementType.TYPE}) |
| 16 | @Retention(RetentionPolicy.RUNTIME) | 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,8 +56,4 @@ public @interface Signature { | ||
| 50 | */ | 56 | */ |
| 51 | String sign() default "sign"; | 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 | package cn.iocoder.yudao.framework.signature.core.aop; | 1 | package cn.iocoder.yudao.framework.signature.core.aop; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | ||
| 4 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | import cn.hutool.crypto.SignUtil; | 6 | import cn.hutool.crypto.SignUtil; |
| 6 | import cn.iocoder.yudao.framework.common.exception.ServiceException; | 7 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 7 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | 8 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 8 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 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 | import cn.iocoder.yudao.framework.signature.core.redis.SignatureRedisDAO; | 11 | import cn.iocoder.yudao.framework.signature.core.redis.SignatureRedisDAO; |
| 11 | import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyWrapper; | 12 | import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyWrapper; |
| 12 | import jakarta.servlet.http.HttpServletRequest; | 13 | import jakarta.servlet.http.HttpServletRequest; |
| @@ -15,7 +16,6 @@ import lombok.extern.slf4j.Slf4j; | @@ -15,7 +16,6 @@ import lombok.extern.slf4j.Slf4j; | ||
| 15 | import org.aspectj.lang.JoinPoint; | 16 | import org.aspectj.lang.JoinPoint; |
| 16 | import org.aspectj.lang.annotation.Aspect; | 17 | import org.aspectj.lang.annotation.Aspect; |
| 17 | import org.aspectj.lang.annotation.Before; | 18 | import org.aspectj.lang.annotation.Before; |
| 18 | -import org.springframework.util.Assert; | ||
| 19 | 19 | ||
| 20 | import java.nio.charset.StandardCharsets; | 20 | import java.nio.charset.StandardCharsets; |
| 21 | import java.util.Map; | 21 | import java.util.Map; |
| @@ -25,7 +25,7 @@ import java.util.TreeMap; | @@ -25,7 +25,7 @@ import java.util.TreeMap; | ||
| 25 | import java.util.concurrent.TimeUnit; | 25 | import java.util.concurrent.TimeUnit; |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | - * 拦截声明了 {@link Signature} 注解的方法,实现签名 | 28 | + * 拦截声明了 {@link ApiSignature} 注解的方法,实现签名 |
| 29 | * | 29 | * |
| 30 | * @author Zhougang | 30 | * @author Zhougang |
| 31 | */ | 31 | */ |
| @@ -37,9 +37,9 @@ public class SignatureAspect { | @@ -37,9 +37,9 @@ public class SignatureAspect { | ||
| 37 | private final SignatureRedisDAO signatureRedisDAO; | 37 | private final SignatureRedisDAO signatureRedisDAO; |
| 38 | 38 | ||
| 39 | @Before("@annotation(signature)") | 39 | @Before("@annotation(signature)") |
| 40 | - public void beforePointCut(JoinPoint joinPoint, Signature signature) { | 40 | + public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) { |
| 41 | if (!verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) { | 41 | if (!verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) { |
| 42 | - log.info("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(), | 42 | + log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(), |
| 43 | joinPoint.getArgs()); | 43 | joinPoint.getArgs()); |
| 44 | String message = StrUtil.blankToDefault(signature.message(), | 44 | String message = StrUtil.blankToDefault(signature.message(), |
| 45 | GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); | 45 | GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); |
| @@ -47,25 +47,22 @@ public class SignatureAspect { | @@ -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 | if (!verifyHeaders(signature, request)) { | 51 | if (!verifyHeaders(signature, request)) { |
| 52 | return false; | 52 | return false; |
| 53 | } | 53 | } |
| 54 | // 校验 appId 是否能获取到对应的 appSecret | 54 | // 校验 appId 是否能获取到对应的 appSecret |
| 55 | String appId = request.getHeader(signature.appId()); | 55 | String appId = request.getHeader(signature.appId()); |
| 56 | String appSecret = signatureRedisDAO.getAppSecret(appId); | 56 | String appSecret = signatureRedisDAO.getAppSecret(appId); |
| 57 | - Assert.notNull(appSecret, "找不到对应的 appSecret"); | 57 | + Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId); |
| 58 | // 请求头 | 58 | // 请求头 |
| 59 | SortedMap<String, String> headersMap = getRequestHeaders(signature, request); | 59 | SortedMap<String, String> headersMap = getRequestHeaders(signature, request); |
| 60 | - // 如:/user/{id} url 带有动态参数的情况 | ||
| 61 | - String urlParams = signature.urlEnable() ? request.getServletPath() : ""; | ||
| 62 | // 请求参数 | 60 | // 请求参数 |
| 63 | String requestParams = getRequestParams(request); | 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 | String clientSignature = request.getHeader(signature.sign()); | 67 | String clientSignature = request.getHeader(signature.sign()); |
| 71 | if (!StrUtil.equals(clientSignature, serverSignature)) { | 68 | if (!StrUtil.equals(clientSignature, serverSignature)) { |
| @@ -73,7 +70,7 @@ public class SignatureAspect { | @@ -73,7 +70,7 @@ public class SignatureAspect { | ||
| 73 | } | 70 | } |
| 74 | String nonce = headersMap.get(signature.nonce()); | 71 | String nonce = headersMap.get(signature.nonce()); |
| 75 | // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) | 72 | // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) |
| 76 | - signatureRedisDAO.setNonce(nonce, signature.expireTime(), TimeUnit.MILLISECONDS); | 73 | + signatureRedisDAO.setNonce(nonce, signature.timeout() * 2L, signature.timeUnit()); |
| 77 | return true; | 74 | return true; |
| 78 | } | 75 | } |
| 79 | 76 | ||
| @@ -87,7 +84,7 @@ public class SignatureAspect { | @@ -87,7 +84,7 @@ public class SignatureAspect { | ||
| 87 | * @param signature signature | 84 | * @param signature signature |
| 88 | * @param request request | 85 | * @param request request |
| 89 | */ | 86 | */ |
| 90 | - private boolean verifyHeaders(Signature signature, HttpServletRequest request) { | 87 | + private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) { |
| 91 | String appId = request.getHeader(signature.appId()); | 88 | String appId = request.getHeader(signature.appId()); |
| 92 | if (StrUtil.isBlank(appId)) { | 89 | if (StrUtil.isBlank(appId)) { |
| 93 | return false; | 90 | return false; |
| @@ -97,7 +94,7 @@ public class SignatureAspect { | @@ -97,7 +94,7 @@ public class SignatureAspect { | ||
| 97 | return false; | 94 | return false; |
| 98 | } | 95 | } |
| 99 | String nonce = request.getHeader(signature.nonce()); | 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 | return false; | 98 | return false; |
| 102 | } | 99 | } |
| 103 | String sign = request.getHeader(signature.sign()); | 100 | String sign = request.getHeader(signature.sign()); |
| @@ -105,7 +102,7 @@ public class SignatureAspect { | @@ -105,7 +102,7 @@ public class SignatureAspect { | ||
| 105 | return false; | 102 | return false; |
| 106 | } | 103 | } |
| 107 | // 其他合法性校验 | 104 | // 其他合法性校验 |
| 108 | - long expireTime = signature.expireTime(); | 105 | + long expireTime = signature.timeUnit().toMillis(signature.timeout()); |
| 109 | long requestTimestamp = Long.parseLong(timestamp); | 106 | long requestTimestamp = Long.parseLong(timestamp); |
| 110 | // 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值) | 107 | // 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值) |
| 111 | long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp); | 108 | long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp); |
| @@ -122,7 +119,7 @@ public class SignatureAspect { | @@ -122,7 +119,7 @@ public class SignatureAspect { | ||
| 122 | * @param request request | 119 | * @param request request |
| 123 | * @return signature params | 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 | SortedMap<String, String> sortedMap = new TreeMap<>(); | 123 | SortedMap<String, String> sortedMap = new TreeMap<>(); |
| 127 | sortedMap.put(signature.appId(), request.getHeader(signature.appId())); | 124 | sortedMap.put(signature.appId(), request.getHeader(signature.appId())); |
| 128 | sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp())); | 125 | sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp())); |
| @@ -154,17 +151,5 @@ public class SignatureAspect { | @@ -154,17 +151,5 @@ public class SignatureAspect { | ||
| 154 | return queryString.substring(1); | 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,8 +42,7 @@ public class SignatureRedisDAO { | ||
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | public void setNonce(String nonce, long time, TimeUnit timeUnit) { | 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 | private static String formatAppIdKey(String key) { | 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 | cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration | 1 | cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration |
| 2 | cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration | 2 | cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration |
| 3 | -cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration | ||
| 4 | \ No newline at end of file | 3 | \ No newline at end of file |
| 4 | +cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration | ||
| 5 | +cn.iocoder.yudao.framework.signature.config.YudaoSignatureAutoConfiguration | ||
| 5 | \ No newline at end of file | 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,10 +23,6 @@ public class CacheRequestBodyWrapper extends HttpServletRequestWrapper { | ||
| 23 | */ | 23 | */ |
| 24 | private final byte[] body; | 24 | private final byte[] body; |
| 25 | 25 | ||
| 26 | - public byte[] getBody() { | ||
| 27 | - return body; | ||
| 28 | - } | ||
| 29 | - | ||
| 30 | public CacheRequestBodyWrapper(HttpServletRequest request) { | 26 | public CacheRequestBodyWrapper(HttpServletRequest request) { |
| 31 | super(request); | 27 | super(request); |
| 32 | body = ServletUtils.getBodyBytes(request); | 28 | body = ServletUtils.getBodyBytes(request); |