diff --git a/admin4j-signature/pom.xml b/admin4j-signature/pom.xml new file mode 100644 index 0000000..9e1bd5f --- /dev/null +++ b/admin4j-signature/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.admin4j + framework + ${revision} + + + com.admin4j.signature + admin4j-signature + ${admin4j-signature.version} + pom + + + 0.8.0 + 8 + 8 + UTF-8 + + + + signature-core + signature-spring-boot-starter + + + \ No newline at end of file diff --git a/admin4j-signature/signature-core/pom.xml b/admin4j-signature/signature-core/pom.xml new file mode 100644 index 0000000..92c9da6 --- /dev/null +++ b/admin4j-signature/signature-core/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.admin4j.signature + admin4j-signature + ${admin4j-signature.version} + + + signature-core + + + 8 + 8 + UTF-8 + + + + + com.admin4j.common + admin4j-common + + + org.springframework + spring-webmvc + provided + + + javax.servlet + javax.servlet-api + provided + + + com.admin4j.common + admin4j-common-spring-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + + \ No newline at end of file diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/AbstractSignature.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/AbstractSignature.java new file mode 100644 index 0000000..d72e368 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/AbstractSignature.java @@ -0,0 +1,205 @@ +package com.admin4j.framework.signature; + +import com.admin4j.framework.signature.annotation.Signature; +import com.admin4j.framework.signature.properties.SignatureProperties; +import com.alibaba.fastjson2.JSONObject; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.util.CollectionUtils; +import org.springframework.util.DigestUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; + +/** + * @author zhougang + * @since 2023/11/10 10:43 + */ +public abstract class AbstractSignature implements SignatureService { + + private static final Logger log = LoggerFactory.getLogger(AbstractSignature.class); + + private final StringRedisTemplate stringRedisTemplate; + + private final SignatureProperties signatureProperties; + + private static final String SIGNATURE_NONCE_REDIS_KEY = "signature:nonce:"; + + public AbstractSignature(StringRedisTemplate stringRedisTemplate, SignatureProperties signatureProperties) { + this.stringRedisTemplate = stringRedisTemplate; + this.signatureProperties = signatureProperties; + } + + /** + * 判断请求是否签名通过 + * + * @param request HttpServletRequest + * @return 是否通过 + */ + @Override + public boolean verify(Signature signature, HttpServletRequest request) throws IOException { + // 根据appId获取appSecret + String appId = request.getHeader(signature.appId().filedName()); + String appSecret; + if (StringUtils.isBlank(appId) || + StringUtils.isBlank(appSecret = getAppSecret(request.getHeader(signature.appId().filedName())))) { + return false; + } + // 根据request 中 header值生成SignatureHeaders实体 + if (!verifyHeaders(signature, request)) { + return false; + } + // 获取全部参数(包括URL和Body上的) + SortedMap allParams = getAllParams(signature, request); + // 生成服务端签名 + String plainText = paramsSplicing(allParams, appSecret); + // 将digest 转换成UTF-8 的 byte[] 后 使用MD5算法加密,最后将生成的md5字符串 + String serverSign = digestEncoder(plainText); + // 客户端签名 + String clientSign = request.getHeader(signature.sign().filedName()); + if (!StringUtils.equals(clientSign, serverSign)) { + return false; + } + String nonce = allParams.get(signature.nonce().filedName()); + // 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2 ) + stringRedisTemplate.opsForValue().set(SIGNATURE_NONCE_REDIS_KEY + nonce, nonce, signatureProperties.getExpireTime() * 2, TimeUnit.MILLISECONDS); + return true; + } + + /* + private SignatureHeaders getSignatureHeaders(Signature signature, HttpServletRequest request) { + SignatureHeaders signatureHeaders = new SignatureHeaders(); + signatureHeaders.setAppId(request.getHeader(signature.appId().filedName())); + signatureHeaders.setAppId(request.getHeader(signature.timestamp().filedName())); + signatureHeaders.setAppId(request.getHeader(signature.nonce().filedName())); + signatureHeaders.setAppId(request.getHeader(signature.sign().filedName())); + return signatureHeaders; + } + */ + + /** + * 1.appId是否合法,appId是否有对应的appSecret。 + * 2.请求是否已经超时,默认10分钟。 + * 3.随机串是否合法,是否在指定时间内已经访问过了。 + * 4.sign是否合法。 + */ + private boolean verifyHeaders(Signature signature, HttpServletRequest request) { + + String timestamp = request.getHeader(signature.timestamp().filedName()); + //Assert.notNull(timestamp, "timestamp cannot be empty"); + if (StringUtils.isBlank(timestamp)) { + return false; + } + + Long expireTime = signatureProperties.getExpireTime(); + //其他合法性校验 + long requestTimestamp = Long.parseLong(timestamp); + // 检查 timestamp 是否超出允许的范围 (重点一:此处需要取绝对值) + long timestampDisparity = Math.abs(System.currentTimeMillis() - requestTimestamp); + //Assert.isTrue(!(timestampDisparity > expireTime), "Request time exceeds the specified limit"); + if (timestampDisparity > expireTime) { + return false; + } + + String nonce = request.getHeader(signature.nonce().filedName()); + //Assert.notNull(nonce, "Random strings cannot be empty"); + if (StringUtils.isBlank(nonce)) { + return false; + } + //Assert.isTrue(!(nonce.length() < 10), "The random string nonce length is at least 10 bits"); + if (nonce.length() < 10) { + return false; + } + String cacheNonce = stringRedisTemplate.opsForValue().get(SIGNATURE_NONCE_REDIS_KEY + nonce); + //Assert.isNull(cacheNonce, "This nonce has already been used and the request is invalid"); + if (StringUtils.isNotBlank(cacheNonce)) { + return false; + } + + String sign = request.getHeader(signature.sign().filedName()); + //Assert.notNull(sign, "sign cannot be empty"); + return StringUtils.isNotBlank(sign); + } + + /** + * 获取全部参数(包括URL和Body上的) + * + * @param request request + * @return + */ + protected SortedMap getAllParams(Signature signature, HttpServletRequest request) throws IOException { + + SortedMap sortedMap = new TreeMap<>(); + + sortedMap.put(signature.appId().filedName(), request.getHeader(signature.appId().filedName())); + sortedMap.put(signature.timestamp().filedName(), request.getHeader(signature.timestamp().filedName())); + sortedMap.put(signature.nonce().filedName(), request.getHeader(signature.nonce().filedName())); + // 有url带动态参数的情况, 所以加上url, 客户端对应也要拼接 + sortedMap.put("url", request.getServletPath()); + + // 获取parameters(对应@RequestParam) + if (!CollectionUtils.isEmpty(request.getParameterMap())) { + Map requestParams = request.getParameterMap(); + //获取GET请求参数,以键值对形式保存 + for (Map.Entry entry : requestParams.entrySet()) { + sortedMap.put(entry.getKey(), entry.getValue()[0]); + } + } + + BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request); + // 分别获取了request input stream中的body信息、parameter信息 + JSONObject data = JSONObject.parseObject(requestWrapper.getBody()); + // 获取POST请求的JSON参数,以键值对形式保存 + for (Map.Entry entry : data.entrySet()) { + sortedMap.put(entry.getKey(), entry.getValue().toString()); + } + + return sortedMap; + } + + /** + * 所有的参数与应用密钥appSecret 进行排序加密后生成签名 + * + * @param sortedMap 根据key升序排序的后所有请求参数 + * @param appSecret 应用id对应的应用密钥 + * @return 生成接口签名 + */ + protected String paramsSplicing(SortedMap sortedMap, String appSecret) { + // 进行key value拼接 + StringBuilder plainText = new StringBuilder(); + for (Map.Entry entry : sortedMap.entrySet()) { + plainText.append(entry.getKey()).append(entry.getValue()); + } + + // 结尾拼接应用密钥 appSecret + plainText.append(appSecret); + + // 摘要 + return plainText.toString(); + } + + /** + * 获取appId对应的secret,假数据 + * + * @param appId 应用id + * @return + */ + protected String getAppSecret(String appId) { + return ""; + } + + /** + * 摘要加密 + * @param plainText + * @return + */ + protected String digestEncoder(String plainText) throws IOException { + return DigestUtils.md5DigestAsHex(StringUtils.getBytes(plainText, "UTF-8")); + } +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/BodyReaderHttpServletRequestWrapper.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/BodyReaderHttpServletRequestWrapper.java new file mode 100644 index 0000000..2f9d999 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/BodyReaderHttpServletRequestWrapper.java @@ -0,0 +1,68 @@ +package com.admin4j.framework.signature; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; + +public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final String body; + + public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { + super(request); + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader bufferedReader = null; + try { + InputStream inputStream = request.getInputStream(); + if (inputStream != null) { + bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + char[] charBuffer = new char[128]; + int bytesRead; + while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); + } + } + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + body = stringBuilder.toString(); + } + + @Override + public ServletInputStream getInputStream() { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() { + return byteArrayInputStream.read(); + } + }; + } + + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } + + public String getBody() { + return this.body; + } +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/DefaultSignature.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/DefaultSignature.java new file mode 100644 index 0000000..de9fe5f --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/DefaultSignature.java @@ -0,0 +1,17 @@ +package com.admin4j.framework.signature; + +import com.admin4j.framework.signature.properties.SignatureProperties; +import org.springframework.data.redis.core.StringRedisTemplate; + +/** + * 签名的默认实现 + * + * @author zhougang + * @since 2023/10/11 13:39 + */ +public class DefaultSignature extends AbstractSignature { + + public DefaultSignature(StringRedisTemplate stringRedisTemplate, SignatureProperties signatureProperties) { + super(stringRedisTemplate, signatureProperties); + } +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/SignatureHeaders.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/SignatureHeaders.java new file mode 100644 index 0000000..1aba494 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/SignatureHeaders.java @@ -0,0 +1,34 @@ +package com.admin4j.framework.signature; + +import lombok.Data; + +@Data +public class SignatureHeaders { + + /** + * 线下分配的值 + * 客户端和服务端各自保存appId对应的appSecret + */ + private String appId; + + /** + * 时间戳,单位: ms + */ + private String timestamp; + + /** + * 流水号【防止重复提交】; (备注:针对查询接口,流水号只用于日志落地,便于后期日志核查; 针对办理类接口需校验流水号在有效期内的唯一性,以避免重复请求) + * => 流水号/随机串:至少16位,有效期内防重复提交 + */ + private String nonce; + + /** + * 签名 + */ + private String sign; + + /** + * 根据appId从服务端获取 + */ + private String appSecret; +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/SignatureService.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/SignatureService.java new file mode 100644 index 0000000..bf89934 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/SignatureService.java @@ -0,0 +1,20 @@ +package com.admin4j.framework.signature; + +import com.admin4j.framework.signature.annotation.Signature; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * @author zhougang + * @since 2023/11/10 9:42 + */ +public interface SignatureService { + + /** + * 验证签名是否通过 + * + * @return 是否限速 + */ + boolean verify(Signature signature, HttpServletRequest request) throws IOException; +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/annotation/Signature.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/annotation/Signature.java new file mode 100644 index 0000000..847ea2f --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/annotation/Signature.java @@ -0,0 +1,35 @@ +package com.admin4j.framework.signature.annotation; + +import com.admin4j.framework.signature.DefaultSignature; +import com.admin4j.framework.signature.SignatureService; + +import java.lang.annotation.*; + + +/** + * 用于标记接口签名 + * + * @author zhougang + * @since 2022/3/24 16:34 + */ +@Inherited +@Documented +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Signature { + + Class signatureClass() default DefaultSignature.class; + + /** + * 签名字段 + * + * @return + */ + SignatureField appId() default @SignatureField(filedName = "appId", order = 0); + + SignatureField timestamp() default @SignatureField(filedName = "timestamp", order = 1); + + SignatureField nonce() default @SignatureField(filedName = "nonce", order = 2); + + SignatureField sign() default @SignatureField(filedName = "sign", order = 3); +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/annotation/SignatureField.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/annotation/SignatureField.java new file mode 100644 index 0000000..8ea295e --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/annotation/SignatureField.java @@ -0,0 +1,27 @@ +package com.admin4j.framework.signature.annotation; + +import java.lang.annotation.*; + +/** + * 签名算法实现 => 指定哪些字段需要进行签名 + */ +@Documented +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SignatureField { + + /** + * 字段:自定义name,对应前端传入的字段名 + * + * @return + */ + String filedName() default ""; + + /** + * 签名顺序 + * + * @return + */ + int order() default 0; + +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/exception/SignatureException.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/exception/SignatureException.java new file mode 100644 index 0000000..f59c030 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/exception/SignatureException.java @@ -0,0 +1,16 @@ +package com.admin4j.framework.signature.exception; + +/** + * @author zhougang + * @since 2023/11/10 17:05 + */ +public class SignatureException extends RuntimeException { + + public SignatureException(String message) { + super(message); + } + + public SignatureException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/filter/RequestReplaceFilter.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/filter/RequestReplaceFilter.java new file mode 100644 index 0000000..890d064 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/filter/RequestReplaceFilter.java @@ -0,0 +1,28 @@ +package com.admin4j.framework.signature.filter; + +import com.admin4j.framework.signature.BodyReaderHttpServletRequestWrapper; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequestWrapper; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 包装HttpServletRequest对象 + * @author zhougang + * @since 2023/11/12 10:23 + */ +public class RequestReplaceFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + if (!(request instanceof ServletRequestWrapper)) { + // 包装HttpServletRequest对象,缓存body数据,再次读取的时候将缓存的值写出,解决HttpServetRequest读取body只能一次的问题 + request = new BodyReaderHttpServletRequestWrapper(request); + } + filterChain.doFilter(request, response); + } +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/interceptor/SignatureInterceptor.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/interceptor/SignatureInterceptor.java new file mode 100644 index 0000000..6283ed4 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/interceptor/SignatureInterceptor.java @@ -0,0 +1,54 @@ +package com.admin4j.framework.signature.interceptor; + +import com.admin4j.framework.signature.SignatureService; +import com.admin4j.framework.signature.annotation.Signature; +import com.admin4j.framework.signature.exception.SignatureException; +import com.admin4j.spring.util.SpringUtils; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * @author zhougang + * @since 2023/11/10 9:51 + */ +public class SignatureInterceptor implements HandlerInterceptor, ApplicationContextAware { + + private ApplicationContext applicationContext; + + public SignatureInterceptor() { + + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception { + if (!(handler instanceof HandlerMethod)) { + return true; + } + HandlerMethod handlerMethod = (HandlerMethod) handler; + + Signature signature = handlerMethod.getMethodAnnotation(Signature.class); + + if (signature == null) { + Class beanType = handlerMethod.getBeanType(); + Signature annotation = beanType.getAnnotation(Signature.class); + if (annotation == null) { + return true; + } + signature = annotation; + } + + SignatureService signatureService = SpringUtils.getBean(signature.signatureClass()); + if (!signatureService.verify(signature, request)) { + throw new SignatureException("Signature failure"); + } + return true; + } +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/properties/SignatureProperties.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/properties/SignatureProperties.java new file mode 100644 index 0000000..31030e2 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/properties/SignatureProperties.java @@ -0,0 +1,25 @@ +package com.admin4j.framework.signature.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Actuator 监控授权配置 + * + * @author zhougang + * @since 2023/11/12 10:57 + */ +@Data +@ConfigurationProperties(prefix = "admin4j.signature") +public class SignatureProperties { + + /** + * 是否开启 + */ + private boolean enabled = false; + + /** + * 同一个请求多长时间内有效 默认10分钟 + */ + private Long expireTime = 600000L; +} diff --git a/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/util/SignatureUtil.java b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/util/SignatureUtil.java new file mode 100644 index 0000000..85f1f21 --- /dev/null +++ b/admin4j-signature/signature-core/src/main/java/com/admin4j/framework/signature/util/SignatureUtil.java @@ -0,0 +1,6 @@ +package com.admin4j.framework.signature.util; + +public class SignatureUtil { + +} + diff --git a/admin4j-signature/signature-spring-boot-starter/pom.xml b/admin4j-signature/signature-spring-boot-starter/pom.xml new file mode 100644 index 0000000..d102de9 --- /dev/null +++ b/admin4j-signature/signature-spring-boot-starter/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + com.admin4j.signature + admin4j-signature + ${admin4j-signature.version} + + + signature-spring-boot-starter + + + 8 + 8 + UTF-8 + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.springframework + spring-webmvc + provided + + + org.springframework.boot + spring-boot-autoconfigure + + + com.admin4j.signature + signature-core + ${admin4j-signature.version} + + + junit + junit + test + + + \ No newline at end of file diff --git a/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/SignatureGlobalExceptionHandler.java b/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/SignatureGlobalExceptionHandler.java new file mode 100644 index 0000000..347de78 --- /dev/null +++ b/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/SignatureGlobalExceptionHandler.java @@ -0,0 +1,34 @@ +package com.admin4j.signature; + +import com.admin4j.common.exception.handler.AbstractExceptionHandler; +import com.admin4j.common.pojo.IResponse; +import com.admin4j.common.pojo.ResponseEnum; +import com.admin4j.common.pojo.SimpleResponse; +import com.admin4j.framework.signature.exception.SignatureException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author zhougang + * @since 2023/11/10 17:58 + */ +@RestControllerAdvice +@Slf4j +public class SignatureGlobalExceptionHandler extends AbstractExceptionHandler { + + @ExceptionHandler(SignatureException.class) + public ResponseEntity distributedLockException(SignatureException e) { + log.error("SignatureException:" + e.getMessage(), e); + + return renderException(e, SimpleResponse.of(ResponseEnum.REQUEST_TOO_MANY_REQUESTS.getCode(), e.getMessage())); + } + + @Override + public ResponseEntity renderException(Exception e, IResponse response) { + publishGlobalExceptionEvent(e); + return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(response); + } +} diff --git a/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/configuration/SignatureAutoConfiguration.java b/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/configuration/SignatureAutoConfiguration.java new file mode 100644 index 0000000..b400f50 --- /dev/null +++ b/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/configuration/SignatureAutoConfiguration.java @@ -0,0 +1,70 @@ +package com.admin4j.signature.configuration; + +import com.admin4j.framework.signature.filter.RequestReplaceFilter; +import com.admin4j.framework.signature.interceptor.SignatureInterceptor; +import com.admin4j.framework.signature.properties.SignatureProperties; +import com.admin4j.signature.SignatureGlobalExceptionHandler; +import org.springframework.beans.BeansException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +/** + * @author zhougang + * @since 2023/11/10 15:10 + */ +@Configuration +@ConditionalOnClass(name = {"org.springframework.web.servlet.HandlerInterceptor"}) +@EnableConfigurationProperties(SignatureProperties.class) +@ConditionalOnProperty(prefix = "admin4j.signature", name = "enabled", matchIfMissing = true) +public class SignatureAutoConfiguration implements ApplicationContextAware { + + @Bean + public RequestReplaceFilter requestReplaceFilter() { + return new RequestReplaceFilter(); + } + + /** + * 配置RequestReplaceFilter对象过滤器 + * + * @return + */ + @Bean + public FilterRegistrationBean filterRegistrationBean(RequestReplaceFilter requestReplaceFilter) { + FilterRegistrationBean filterRegistration = new FilterRegistrationBean<>(requestReplaceFilter); + filterRegistration.addUrlPatterns("/*"); + filterRegistration.setOrder(2); + return filterRegistration; + } + + @Bean + public SignatureInterceptor signatureInterceptor() { + return new SignatureInterceptor(); + } + + @Bean + public SignatureWebMvcConfigurer signatureWebMvcConfigurer(SignatureInterceptor signatureInterceptor) { + return new SignatureWebMvcConfigurer(signatureInterceptor); + } + + @Bean + @Order(2) + @ConditionalOnClass(name = "com.admin4j.common.pojo.SimpleResponse") + public SignatureGlobalExceptionHandler signatureGlobalExceptionHandler() { + return new SignatureGlobalExceptionHandler(); + } + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/configuration/SignatureWebMvcConfigurer.java b/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/configuration/SignatureWebMvcConfigurer.java new file mode 100644 index 0000000..0172cb4 --- /dev/null +++ b/admin4j-signature/signature-spring-boot-starter/src/main/java/com/admin4j/signature/configuration/SignatureWebMvcConfigurer.java @@ -0,0 +1,31 @@ +package com.admin4j.signature.configuration; + +import com.admin4j.framework.signature.interceptor.SignatureInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author zhougang + * @since 2023/11/10 15:12 + */ +public class SignatureWebMvcConfigurer implements WebMvcConfigurer { + + private SignatureInterceptor signatureInterceptor; + + public SignatureWebMvcConfigurer(SignatureInterceptor signatureInterceptor) { + this.signatureInterceptor = signatureInterceptor; + } + + /** + * Add Spring MVC lifecycle interceptors for pre- and post-processing of + * controller method invocations and resource handler requests. + * Interceptors can be registered to apply to all requests or be limited + * to a subset of URL patterns. + * + * @param registry + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(signatureInterceptor).addPathPatterns("/**"); + } +} diff --git a/admin4j-signature/signature-spring-boot-starter/src/main/resources/META-INF/spring.factories b/admin4j-signature/signature-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..bc4944a --- /dev/null +++ b/admin4j-signature/signature-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.admin4j.signature.configuration.SignatureAutoConfiguration,\ + com.admin4j.framework.signature.DefaultSignature \ No newline at end of file diff --git a/pom.xml b/pom.xml index ccd763d..46967ad 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,7 @@ excel-spring-boot-starter admin4j-limiter admin4j-redis + admin4j-signature elasticsearch-spring-boot-starter mybatis-plus-boot-starter