Merge remote-tracking branch 'base-version/main' into dev
This commit is contained in:
@@ -1,11 +1,20 @@
|
||||
package com.zt.plat.framework.signature.config;
|
||||
|
||||
import com.zt.plat.framework.redis.config.ZtRedisAutoConfiguration;
|
||||
import com.zt.plat.framework.signature.core.aop.ApiSignatureAspect;
|
||||
import com.zt.plat.framework.signature.core.ApiSignatureVerifier;
|
||||
import com.zt.plat.framework.signature.core.config.ApiSignatureProperties;
|
||||
import com.zt.plat.framework.signature.core.redis.ApiSignatureRedisDAO;
|
||||
import com.zt.plat.framework.signature.core.web.ApiSignatureHandlerInterceptor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HTTP API 签名的自动配置类
|
||||
@@ -13,16 +22,47 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
* @author Zhougang
|
||||
*/
|
||||
@AutoConfiguration(after = ZtRedisAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(ApiSignatureProperties.class)
|
||||
public class ZtApiSignatureAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {
|
||||
return new ApiSignatureAspect(signatureRedisDAO);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) {
|
||||
return new ApiSignatureRedisDAO(stringRedisTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApiSignatureVerifier apiSignatureVerifier(ApiSignatureRedisDAO signatureRedisDAO) {
|
||||
return new ApiSignatureVerifier(signatureRedisDAO);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApiSignatureHandlerInterceptor apiSignatureHandlerInterceptor(ApiSignatureVerifier verifier,
|
||||
ApiSignatureProperties properties) {
|
||||
return new ApiSignatureHandlerInterceptor(verifier, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer apiSignatureWebMvcConfigurer(ApiSignatureHandlerInterceptor interceptor,
|
||||
ApiSignatureProperties properties) {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
if (!properties.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
InterceptorRegistration registration = registry.addInterceptor(interceptor);
|
||||
List<String> includePaths = properties.getIncludePaths();
|
||||
if (CollectionUtils.isEmpty(includePaths)) {
|
||||
registration.addPathPatterns("/**");
|
||||
} else {
|
||||
registration.addPathPatterns(includePaths.toArray(new String[0]));
|
||||
}
|
||||
List<String> excludePaths = properties.getExcludePaths();
|
||||
if (!CollectionUtils.isEmpty(excludePaths)) {
|
||||
registration.excludePathPatterns(excludePaths.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
package com.zt.plat.framework.signature.core.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* API 签名配置
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "zt.api-signature")
|
||||
public class ApiSignatureProperties {
|
||||
|
||||
/**
|
||||
* 是否开启全局签名校验
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 签名有效期
|
||||
*/
|
||||
private int timeout = 60;
|
||||
|
||||
/**
|
||||
* 时间单位
|
||||
*/
|
||||
private TimeUnit timeUnit = TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* 校验失败时的提示信息
|
||||
*/
|
||||
private String message = "签名不正确";
|
||||
|
||||
/**
|
||||
* 请求头:appId
|
||||
*/
|
||||
private String appId = "appId";
|
||||
|
||||
/**
|
||||
* 请求头:timestamp
|
||||
*/
|
||||
private String timestamp = "timestamp";
|
||||
|
||||
/**
|
||||
* 请求头:nonce
|
||||
*/
|
||||
private String nonce = "nonce";
|
||||
|
||||
/**
|
||||
* 请求头:sign
|
||||
*/
|
||||
private String sign = "sign";
|
||||
|
||||
/**
|
||||
* 需要进行签名校验的路径,默认全量
|
||||
*/
|
||||
private List<String> includePaths = new ArrayList<>(Arrays.asList("/**"));
|
||||
|
||||
/**
|
||||
* 无需签名校验的路径
|
||||
*/
|
||||
private List<String> excludePaths = new ArrayList<>(Arrays.asList("/error", "/swagger-ui/**", "/v3/api-docs/**"));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.zt.plat.framework.signature.core.model;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zt.plat.framework.signature.core.annotation.ApiSignature;
|
||||
import com.zt.plat.framework.signature.core.config.ApiSignatureProperties;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 签名校验规则
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
public class ApiSignatureRule {
|
||||
|
||||
private final int timeout;
|
||||
private final TimeUnit timeUnit;
|
||||
private final String message;
|
||||
private final String appId;
|
||||
private final String timestamp;
|
||||
private final String nonce;
|
||||
private final String sign;
|
||||
|
||||
public static ApiSignatureRule from(ApiSignatureProperties properties) {
|
||||
return ApiSignatureRule.builder()
|
||||
.timeout(properties.getTimeout())
|
||||
.timeUnit(properties.getTimeUnit())
|
||||
.message(properties.getMessage())
|
||||
.appId(properties.getAppId())
|
||||
.timestamp(properties.getTimestamp())
|
||||
.nonce(properties.getNonce())
|
||||
.sign(properties.getSign())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ApiSignatureRule from(ApiSignature signature, ApiSignatureProperties defaults) {
|
||||
return ApiSignatureRule.builder()
|
||||
.timeout(signature.timeout())
|
||||
.timeUnit(signature.timeUnit())
|
||||
.message(StrUtil.blankToDefault(signature.message(), defaults.getMessage()))
|
||||
.appId(signature.appId())
|
||||
.timestamp(signature.timestamp())
|
||||
.nonce(signature.nonce())
|
||||
.sign(signature.sign())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.zt.plat.framework.signature.core.web;
|
||||
|
||||
import com.zt.plat.framework.signature.core.ApiSignatureVerifier;
|
||||
import com.zt.plat.framework.signature.core.annotation.ApiSignature;
|
||||
import com.zt.plat.framework.signature.core.config.ApiSignatureProperties;
|
||||
import com.zt.plat.framework.signature.core.model.ApiSignatureRule;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 全局 API 签名拦截器
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ApiSignatureHandlerInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final ApiSignatureVerifier verifier;
|
||||
private final ApiSignatureProperties properties;
|
||||
private final AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||
private final UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
if (!properties.isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
return true;
|
||||
}
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
if (shouldSkip(lookupPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ApiSignatureRule rule = createRule(handlerMethod);
|
||||
verifier.verify(rule, request);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shouldSkip(String path) {
|
||||
List<String> includePaths = properties.getIncludePaths();
|
||||
if (!CollectionUtils.isEmpty(includePaths)) {
|
||||
boolean matched = includePaths.stream().anyMatch(pattern -> pathMatcher.match(pattern, path));
|
||||
if (!matched) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
List<String> excludePaths = properties.getExcludePaths();
|
||||
if (!CollectionUtils.isEmpty(excludePaths)) {
|
||||
for (String pattern : excludePaths) {
|
||||
if (pathMatcher.match(pattern, path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private ApiSignatureRule createRule(HandlerMethod handlerMethod) {
|
||||
ApiSignature signature = handlerMethod.getMethodAnnotation(ApiSignature.class);
|
||||
if (signature != null) {
|
||||
return ApiSignatureRule.from(signature, properties);
|
||||
}
|
||||
signature = handlerMethod.getBeanType().getAnnotation(ApiSignature.class);
|
||||
if (signature != null) {
|
||||
return ApiSignatureRule.from(signature, properties);
|
||||
}
|
||||
return ApiSignatureRule.from(properties);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
com.zt.plat.framework.idempotent.config.ZtIdempotentConfiguration
|
||||
com.zt.plat.framework.lock4j.config.ZtLock4jConfiguration
|
||||
com.zt.plat.framework.ratelimiter.config.ZtRateLimiterConfiguration
|
||||
com.zt.plat.framework.signature.config.ZtApiSignatureAutoConfiguration
|
||||
Reference in New Issue
Block a user