【新增】优化部分代码风格

This commit is contained in:
zhougang 2024-05-28 15:09:01 +08:00
parent 5bc0190194
commit 5f278ac23b
5 changed files with 26 additions and 43 deletions

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.signature.core.annotation;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
@ -14,12 +15,17 @@ import java.lang.annotation.*;
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Signature {
public @interface ApiSignature {
/**
* 同一个请求多长时间内有效 默认 10分钟
* 同一个请求多长时间内有效 默认 60
*/
long expireTime() default 600000L;
int timeout() default 60;
/**
* 时间单位默认为 SECONDS
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
// ========================== 签名参数 ==========================
@ -50,8 +56,4 @@ public @interface Signature {
*/
String sign() default "sign";
/**
* url 客户端不需要传递但是可以用来加签( /{id} 带有动态参数的 url 如果没有动态参数可设置为 false 不进行加签)
*/
boolean urlEnable() default true;
}

View File

@ -1,12 +1,13 @@
package cn.iocoder.yudao.framework.signature.core.aop;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SignUtil;
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.Signature;
import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
import cn.iocoder.yudao.framework.signature.core.redis.SignatureRedisDAO;
import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyWrapper;
import jakarta.servlet.http.HttpServletRequest;
@ -15,7 +16,6 @@ import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.util.Assert;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@ -25,7 +25,7 @@ import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
/**
* 拦截声明了 {@link Signature} 注解的方法实现签名
* 拦截声明了 {@link ApiSignature} 注解的方法实现签名
*
* @author Zhougang
*/
@ -37,9 +37,9 @@ public class SignatureAspect {
private final SignatureRedisDAO signatureRedisDAO;
@Before("@annotation(signature)")
public void beforePointCut(JoinPoint joinPoint, Signature signature) {
public void beforePointCut(JoinPoint joinPoint, ApiSignature signature) {
if (!verifySignature(signature, Objects.requireNonNull(ServletUtils.getRequest()))) {
log.info("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
log.error("[beforePointCut][方法{} 参数({}) 签名失败]", joinPoint.getSignature().toString(),
joinPoint.getArgs());
String message = StrUtil.blankToDefault(signature.message(),
GlobalErrorCodeConstants.BAD_REQUEST.getMsg());
@ -47,25 +47,22 @@ public class SignatureAspect {
}
}
private boolean verifySignature(Signature signature, HttpServletRequest request) {
private boolean verifySignature(ApiSignature signature, HttpServletRequest request) {
if (!verifyHeaders(signature, request)) {
return false;
}
// 校验 appId 是否能获取到对应的 appSecret
String appId = request.getHeader(signature.appId());
String appSecret = signatureRedisDAO.getAppSecret(appId);
Assert.notNull(appSecret, "找不到对应的 appSecret");
Assert.notNull(appSecret, "[appId({})] 找不到对应的 appSecret", appId);
// 请求头
SortedMap<String, String> headersMap = getRequestHeaders(signature, request);
// /user/{id} url 带有动态参数的情况
String urlParams = signature.urlEnable() ? request.getServletPath() : "";
// 请求参数
String requestParams = getRequestParams(request);
// 请求体
String requestBody = getRequestBody(request);
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : "";
// 生成服务端签名
String serverSignature = SignUtil.signParamsSha256(headersMap,
urlParams + requestParams + requestBody + appSecret);
String serverSignature = SignUtil.signParamsSha256(headersMap, requestParams + requestBody + appSecret);
// 客户端签名
String clientSignature = request.getHeader(signature.sign());
if (!StrUtil.equals(clientSignature, serverSignature)) {
@ -73,7 +70,7 @@ public class SignatureAspect {
}
String nonce = headersMap.get(signature.nonce());
// nonce 记入缓存防止重复使用重点二此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2
signatureRedisDAO.setNonce(nonce, signature.expireTime(), TimeUnit.MILLISECONDS);
signatureRedisDAO.setNonce(nonce, signature.timeout() * 2L, signature.timeUnit());
return true;
}
@ -87,7 +84,7 @@ public class SignatureAspect {
* @param signature signature
* @param request request
*/
private boolean verifyHeaders(Signature signature, HttpServletRequest request) {
private boolean verifyHeaders(ApiSignature signature, HttpServletRequest request) {
String appId = request.getHeader(signature.appId());
if (StrUtil.isBlank(appId)) {
return false;
@ -97,7 +94,7 @@ public class SignatureAspect {
return false;
}
String nonce = request.getHeader(signature.nonce());
if (StrUtil.isBlank(nonce) || nonce.length() < 10) {
if (StrUtil.isBlank(nonce) || StrUtil.length(nonce) < 10) {
return false;
}
String sign = request.getHeader(signature.sign());
@ -105,7 +102,7 @@ public class SignatureAspect {
return false;
}
// 其他合法性校验
long expireTime = signature.expireTime();
long expireTime = signature.timeUnit().toMillis(signature.timeout());
long requestTimestamp = Long.parseLong(timestamp);
// 检查 timestamp 是否超出允许的范围 重点一此处需要取绝对值
long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp);
@ -122,7 +119,7 @@ public class SignatureAspect {
* @param request request
* @return signature params
*/
private SortedMap<String, String> getRequestHeaders(Signature signature, HttpServletRequest request) {
private SortedMap<String, String> getRequestHeaders(ApiSignature signature, HttpServletRequest request) {
SortedMap<String, String> sortedMap = new TreeMap<>();
sortedMap.put(signature.appId(), request.getHeader(signature.appId()));
sortedMap.put(signature.timestamp(), request.getHeader(signature.timestamp()));
@ -154,17 +151,5 @@ public class SignatureAspect {
return queryString.substring(1);
}
/**
* 获取请求体参数
*
* @param request request
* @return body
*/
private String getRequestBody(HttpServletRequest request) {
CacheRequestBodyWrapper requestWrapper = new CacheRequestBodyWrapper(request);
// 获取 body
return new String(requestWrapper.getBody(), StandardCharsets.UTF_8);
}
}

View File

@ -42,8 +42,7 @@ public class SignatureRedisDAO {
}
public void setNonce(String nonce, long time, TimeUnit timeUnit) {
// nonce 记入缓存防止重复使用重点二此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2
stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), nonce, time * 2, timeUnit);
stringRedisTemplate.opsForValue().set(formatNonceKey(nonce), nonce, time, timeUnit);
}
private static String formatAppIdKey(String key) {

View File

@ -1,3 +1,4 @@
cn.iocoder.yudao.framework.idempotent.config.YudaoIdempotentConfiguration
cn.iocoder.yudao.framework.lock4j.config.YudaoLock4jConfiguration
cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration
cn.iocoder.yudao.framework.ratelimiter.config.YudaoRateLimiterConfiguration
cn.iocoder.yudao.framework.signature.config.YudaoSignatureAutoConfiguration

View File

@ -23,10 +23,6 @@ public class CacheRequestBodyWrapper extends HttpServletRequestWrapper {
*/
private final byte[] body;
public byte[] getBody() {
return body;
}
public CacheRequestBodyWrapper(HttpServletRequest request) {
super(request);
body = ServletUtils.getBodyBytes(request);