From 931ed0eb0036d3505fd0a4bd7b2f1d54c7d5f903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Sat, 18 Sep 2021 15:44:36 +0800 Subject: [PATCH] =?UTF-8?q?update=20[=E9=87=8D=E5=A4=A7=E6=94=B9=E5=8A=A8]?= =?UTF-8?q?=E9=87=8D=E5=86=99=20=E9=98=B2=E9=87=8D=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20=E4=BD=BF=E7=94=A8=E5=88=86=E5=B8=83?= =?UTF-8?q?=E5=BC=8F=E9=94=81=20=E8=A7=A3=E5=86=B3=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20=E5=8E=8B=E6=B5=8B=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/aspectj/RepeatSubmitAspect.java | 93 ++++++++++++++ .../framework/config/ResourcesConfig.java | 27 +---- .../interceptor/RepeatSubmitInterceptor.java | 50 -------- .../impl/SameUrlDataInterceptor.java | 114 ------------------ 4 files changed, 98 insertions(+), 186 deletions(-) create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java new file mode 100644 index 000000000..b91b2e301 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java @@ -0,0 +1,93 @@ +package com.ruoyi.framework.aspectj; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.properties.TokenProperties; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.config.properties.RepeatSubmitProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +/** + * 防止重复提交 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@Aspect +@Component +public class RepeatSubmitAspect { + + private final TokenProperties tokenProperties; + private final RepeatSubmitProperties repeatSubmitProperties; + private final LockTemplate lockTemplate; + + // 配置织入点 + @Pointcut("@annotation(com.ruoyi.common.annotation.RepeatSubmit)") + public void repeatSubmitPointCut() { + } + + @Before("repeatSubmitPointCut()") + public void doBefore(JoinPoint point) throws Throwable { + RepeatSubmit repeatSubmit = getAnnotationRateLimiter(point); + // 如果注解不为0 则使用注解数值 + long intervalTime = repeatSubmitProperties.getIntervalTime(); + if (repeatSubmit.intervalTime() > 0) { + intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime()); + } + if (intervalTime < 1000) { + throw new ServiceException("重复提交间隔时间不能小于'1'秒"); + } + HttpServletRequest request = ServletUtils.getRequest(); + String nowParams = StrUtil.join(",", point.getArgs()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(tokenProperties.getHeader()); + if (StringUtils.isEmpty(submitKey)) { + submitKey = url; + } + submitKey = SecureUtil.md5(submitKey + ":" + nowParams); + // 唯一标识(指定key + 消息头) + String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; + LockInfo lock = lockTemplate.lock(cacheRepeatKey, intervalTime, intervalTime / 2); + if (lock == null) { + throw new ServiceException("不允许重复提交,请稍后再试!"); + } + } + + /** + * 是否存在注解,如果存在就获取 + */ + private RepeatSubmit getAnnotationRateLimiter(JoinPoint joinPoint) { + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + + if (method != null) { + return method.getAnnotation(RepeatSubmit.class); + } + return null; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java index 870af3f9a..ddf4c70b3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -1,52 +1,35 @@ package com.ruoyi.framework.config; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 通用配置 * - * @author ruoyi + * @author Lion Li */ @Configuration -public class ResourcesConfig implements WebMvcConfigurer -{ - @Autowired - private RepeatSubmitInterceptor repeatSubmitInterceptor; +public class ResourcesConfig implements WebMvcConfigurer { @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) - { - } - - /** - * 自定义拦截规则 - */ - @Override - public void addInterceptors(InterceptorRegistry registry) - { - registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + public void addResourceHandlers(ResourceHandlerRegistry registry) { } /** * 跨域配置 */ @Bean - public CorsFilter corsFilter() - { + public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 设置访问源地址 - config.addAllowedOriginPattern("*"); + config.addAllowedOriginPattern("*"); // 设置访问源请求头 config.addAllowedHeader("*"); // 设置访问源请求方法 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java deleted file mode 100644 index 9af0a830b..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ruoyi.framework.interceptor; - -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.JsonUtils; -import com.ruoyi.common.utils.ServletUtils; -import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.lang.reflect.Method; - -/** - * 防止重复提交拦截器 - * - * 移除继承 HandlerInterceptorAdapter 过期类 - * 改为实现 HandlerInterceptor 接口(官方推荐写法) - * - * @author Lion Li - */ -@Component -public abstract class RepeatSubmitInterceptor implements HandlerInterceptor { - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception { - if (handler instanceof HandlerMethod) { - HandlerMethod handlerMethod = (HandlerMethod) handler; - Method method = handlerMethod.getMethod(); - RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); - if (annotation != null) { - if (this.isRepeatSubmit(annotation, request)) { - AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试"); - ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult)); - return false; - } - } - return true; - } else { - return HandlerInterceptor.super.preHandle(request, response, handler); - } - } - - /** - * 验证是否重复提交由子类实现具体的防重复提交的规则 - */ - public abstract boolean isRepeatSubmit(RepeatSubmit annotation, HttpServletRequest request); -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java deleted file mode 100644 index 47b7fbb69..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.ruoyi.framework.interceptor.impl; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.io.IoUtil; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.filter.RepeatedlyRequestWrapper; -import com.ruoyi.common.utils.JsonUtils; -import com.ruoyi.common.utils.RedisUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.config.properties.RepeatSubmitProperties; -import com.ruoyi.common.properties.TokenProperties; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * 判断请求url和数据是否和上一次相同, - * 如果和上次相同,则是重复提交表单。 - * - * @author Lion Li - */ -@Slf4j -@RequiredArgsConstructor(onConstructor_ = @Autowired) -@Component -public class SameUrlDataInterceptor extends RepeatSubmitInterceptor { - public final String REPEAT_PARAMS = "repeatParams"; - - public final String REPEAT_TIME = "repeatTime"; - - private final TokenProperties tokenProperties; - private final RepeatSubmitProperties repeatSubmitProperties; - - - @SuppressWarnings("unchecked") - @Override - public boolean isRepeatSubmit(RepeatSubmit repeatSubmit, HttpServletRequest request) { - // 如果注解不为0 则使用注解数值 - long intervalTime = repeatSubmitProperties.getIntervalTime(); - if (repeatSubmit.intervalTime() > 0) { - intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime()); - } - String nowParams = ""; - if (request instanceof RepeatedlyRequestWrapper) { - RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; - try { - nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream()); - } catch (IOException e) { - log.warn("读取流出现问题!"); - } - } - - // body参数为空,获取Parameter的数据 - if (StringUtils.isEmpty(nowParams)) { - nowParams = JsonUtils.toJsonString(request.getParameterMap()); - } - Map nowDataMap = new HashMap(); - nowDataMap.put(REPEAT_PARAMS, nowParams); - nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); - - // 请求地址(作为存放cache的key值) - String url = request.getRequestURI(); - - // 唯一值(没有消息头则使用请求地址) - String submitKey = request.getHeader(tokenProperties.getHeader()); - if (StringUtils.isEmpty(submitKey)) { - submitKey = url; - } - - // 唯一标识(指定key + 消息头) - String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; - - Object sessionObj = RedisUtils.getCacheObject(cacheRepeatKey); - if (sessionObj != null) { - Map sessionMap = (Map) sessionObj; - if (sessionMap.containsKey(url)) { - Map preDataMap = (Map) sessionMap.get(url); - if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, intervalTime)) { - return true; - } - } - } - Map cacheMap = new HashMap(); - cacheMap.put(url, nowDataMap); - RedisUtils.setCacheObject(cacheRepeatKey, cacheMap, Convert.toInt(intervalTime), TimeUnit.MILLISECONDS); - return false; - } - - /** - * 判断参数是否相同 - */ - private boolean compareParams(Map nowMap, Map preMap) { - String nowParams = (String) nowMap.get(REPEAT_PARAMS); - String preParams = (String) preMap.get(REPEAT_PARAMS); - return nowParams.equals(preParams); - } - - /** - * 判断两次间隔时间 - */ - private boolean compareTime(Map nowMap, Map preMap, long intervalTime) { - long time1 = (Long) nowMap.get(REPEAT_TIME); - long time2 = (Long) preMap.get(REPEAT_TIME); - return (time1 - time2) < intervalTime; - } -}