Преглед изворни кода

update 重写 防重提交拦截器 支持全局与注解自定义 拦截时间配置配置 优化逻辑

疯狂的狮子li пре 4 година
родитељ
комит
c65acd6a28

+ 5 - 0
ruoyi-admin/src/main/resources/application.yml

@@ -107,6 +107,11 @@ token:
   # 令牌有效期(默认30分钟)
   expireTime: 30
 
+# 重复提交
+repeat-submit:
+  # 全局间隔时间(毫秒)
+  intervalTime: 1000
+
 # MyBatisPlus配置
 # https://baomidou.com/config/
 mybatis-plus:

+ 29 - 23
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java

@@ -1,23 +1,29 @@
-package com.ruoyi.common.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * 自定义注解防止表单重复提交
- * 
- * @author ruoyi
- *
- */
-@Inherited
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface RepeatSubmit
-{
-
-}
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 自定义注解防止表单重复提交
+ *
+ * @author Lion Li
+ */
+@Inherited
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface RepeatSubmit {
+
+	/**
+	 * 默认使用全局配置
+	 */
+	int intervalTime() default 0;
+
+	TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
+
+}

+ 30 - 36
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java

@@ -6,7 +6,7 @@ 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.handler.HandlerInterceptorAdapter;
+import org.springframework.web.servlet.HandlerInterceptor;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -15,42 +15,36 @@ import java.lang.reflect.Method;
 /**
  * 防止重复提交拦截器
  *
- * @author ruoyi
+ * 移除继承 HandlerInterceptorAdapter 过期类
+ * 改为实现 HandlerInterceptor 接口(官方推荐写法)
+ *
+ * @author Lion Li
  */
 @Component
-public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
-{
-    @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(request))
-                {
-                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
-                    ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
-                    return false;
-                }
-            }
-            return true;
-        }
-        else
-        {
-            return super.preHandle(request, response, handler);
-        }
-    }
+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);
+		}
+	}
 
-    /**
-     * 验证是否重复提交由子类实现具体的防重复提交的规则
-     *
-     * @param request
-     * @return
-     * @throws Exception
-     */
-    public abstract boolean isRepeatSubmit(HttpServletRequest request);
+	/**
+	 * 验证是否重复提交由子类实现具体的防重复提交的规则
+	 */
+	public abstract boolean isRepeatSubmit(RepeatSubmit annotation, HttpServletRequest request);
 }

+ 75 - 93
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java

@@ -1,15 +1,19 @@
 package com.ruoyi.framework.interceptor.impl;
 
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.lang.Validator;
+import com.ruoyi.common.annotation.RepeatSubmit;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
 import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
+import com.ruoyi.framework.config.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.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
 import javax.servlet.http.HttpServletRequest;
@@ -20,45 +24,34 @@ import java.util.concurrent.TimeUnit;
 
 /**
  * 判断请求url和数据是否和上一次相同,
- * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
+ * 如果和上次相同,则是重复提交表单。
  *
- * @author ruoyi
+ * @author Lion Li
  */
 @Slf4j
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
 @Component
-public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
-{
-    public final String REPEAT_PARAMS = "repeatParams";
+public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
+	public final String REPEAT_PARAMS = "repeatParams";
 
-    public final String REPEAT_TIME = "repeatTime";
+	public final String REPEAT_TIME = "repeatTime";
 
-    // 令牌自定义标识
-    @Value("${token.header}")
-    private String header;
+	private final TokenProperties tokenProperties;
+	private final RepeatSubmitProperties repeatSubmitProperties;
+	private final RedisCache redisCache;
 
-    @Autowired
-    private RedisCache redisCache;
 
-    /**
-     * 间隔时间,单位:秒 默认10秒
-     *
-     * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
-     */
-    private int intervalTime = 10;
-
-    public void setIntervalTime(int intervalTime)
-    {
-        this.intervalTime = intervalTime;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public boolean isRepeatSubmit(HttpServletRequest request)
-    {
-        String nowParams = "";
-        if (request instanceof RepeatedlyRequestWrapper)
-        {
-            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
+	@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) {
@@ -66,68 +59,57 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
 			}
 		}
 
-        // body参数为空,获取Parameter的数据
-        if (Validator.isEmpty(nowParams))
-        {
-            nowParams = JsonUtils.toJsonString(request.getParameterMap());
-        }
-        Map<String, Object> nowDataMap = new HashMap<String, Object>();
-        nowDataMap.put(REPEAT_PARAMS, nowParams);
-        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
-
-        // 请求地址(作为存放cache的key值)
-        String url = request.getRequestURI();
-
-        // 唯一值(没有消息头则使用请求地址)
-        String submitKey = request.getHeader(header);
-        if (Validator.isEmpty(submitKey))
-        {
-            submitKey = url;
-        }
-
-        // 唯一标识(指定key + 消息头)
-        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
+		// body参数为空,获取Parameter的数据
+		if (Validator.isEmpty(nowParams)) {
+			nowParams = JsonUtils.toJsonString(request.getParameterMap());
+		}
+		Map<String, Object> nowDataMap = new HashMap<String, Object>();
+		nowDataMap.put(REPEAT_PARAMS, nowParams);
+		nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
 
-        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
-        if (sessionObj != null)
-        {
-            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
-            if (sessionMap.containsKey(url))
-            {
-                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
-                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
-                {
-                    return true;
-                }
-            }
-        }
-        Map<String, Object> cacheMap = new HashMap<String, Object>();
-        cacheMap.put(url, nowDataMap);
-        redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS);
-        return false;
-    }
+		// 请求地址(作为存放cache的key值)
+		String url = request.getRequestURI();
 
-    /**
-     * 判断参数是否相同
-     */
-    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
-    {
-        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
-        String preParams = (String) preMap.get(REPEAT_PARAMS);
-        return nowParams.equals(preParams);
-    }
+		// 唯一值(没有消息头则使用请求地址)
+		String submitKey = request.getHeader(tokenProperties.getHeader());
+		if (Validator.isEmpty(submitKey)) {
+			submitKey = url;
+		}
 
-    /**
-     * 判断两次间隔时间
-     */
-    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
-    {
-        long time1 = (Long) nowMap.get(REPEAT_TIME);
-        long time2 = (Long) preMap.get(REPEAT_TIME);
-        if ((time1 - time2) < (this.intervalTime * 1000))
-        {
-            return true;
-        }
-        return false;
-    }
+		// 唯一标识(指定key + 消息头)
+		String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
+
+		Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
+		if (sessionObj != null) {
+			Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
+			if (sessionMap.containsKey(url)) {
+				Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
+				if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, intervalTime)) {
+					return true;
+				}
+			}
+		}
+		Map<String, Object> cacheMap = new HashMap<String, Object>();
+		cacheMap.put(url, nowDataMap);
+		redisCache.setCacheObject(cacheRepeatKey, cacheMap, Convert.toInt(intervalTime), TimeUnit.MILLISECONDS);
+		return false;
+	}
+
+	/**
+	 * 判断参数是否相同
+	 */
+	private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
+		String nowParams = (String) nowMap.get(REPEAT_PARAMS);
+		String preParams = (String) preMap.get(REPEAT_PARAMS);
+		return nowParams.equals(preParams);
+	}
+
+	/**
+	 * 判断两次间隔时间
+	 */
+	private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, long intervalTime) {
+		long time1 = (Long) nowMap.get(REPEAT_TIME);
+		long time2 = (Long) preMap.get(REPEAT_TIME);
+		return (time1 - time2) < intervalTime;
+	}
 }