作者 RuoYi

新增@RepeatSubmit注解,防止重复提交

1 package com.ruoyi.framework.config; 1 package com.ruoyi.framework.config;
2 2
  3 +import org.springframework.beans.factory.annotation.Autowired;
3 import org.springframework.context.annotation.Configuration; 4 import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
4 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 6 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
5 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6 import com.ruoyi.common.constant.Constants; 8 import com.ruoyi.common.constant.Constants;
  9 +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
7 10
8 /** 11 /**
9 * 通用配置 12 * 通用配置
@@ -13,6 +16,9 @@ import com.ruoyi.common.constant.Constants; @@ -13,6 +16,9 @@ import com.ruoyi.common.constant.Constants;
13 @Configuration 16 @Configuration
14 public class ResourcesConfig implements WebMvcConfigurer 17 public class ResourcesConfig implements WebMvcConfigurer
15 { 18 {
  19 + @Autowired
  20 + private RepeatSubmitInterceptor repeatSubmitInterceptor;
  21 +
16 @Override 22 @Override
17 public void addResourceHandlers(ResourceHandlerRegistry registry) 23 public void addResourceHandlers(ResourceHandlerRegistry registry)
18 { 24 {
@@ -23,4 +29,13 @@ public class ResourcesConfig implements WebMvcConfigurer @@ -23,4 +29,13 @@ public class ResourcesConfig implements WebMvcConfigurer
23 registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); 29 registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
24 registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); 30 registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
25 } 31 }
  32 +
  33 + /**
  34 + * 自定义拦截规则
  35 + */
  36 + @Override
  37 + public void addInterceptors(InterceptorRegistry registry)
  38 + {
  39 + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
  40 + }
26 } 41 }
  1 +package com.ruoyi.framework.interceptor;
  2 +
  3 +import java.lang.reflect.Method;
  4 +import javax.servlet.http.HttpServletRequest;
  5 +import javax.servlet.http.HttpServletResponse;
  6 +import org.springframework.stereotype.Component;
  7 +import org.springframework.web.method.HandlerMethod;
  8 +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  9 +import com.alibaba.fastjson.JSONObject;
  10 +import com.ruoyi.common.utils.ServletUtils;
  11 +import com.ruoyi.framework.interceptor.annotation.RepeatSubmit;
  12 +import com.ruoyi.framework.web.domain.AjaxResult;
  13 +
  14 +/**
  15 + * 防止重复提交拦截器
  16 + *
  17 + * @author ruoyi
  18 + */
  19 +@Component
  20 +public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
  21 +{
  22 + @Override
  23 + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
  24 + {
  25 + if (handler instanceof HandlerMethod)
  26 + {
  27 + HandlerMethod handlerMethod = (HandlerMethod) handler;
  28 + Method method = handlerMethod.getMethod();
  29 + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
  30 + if (annotation != null)
  31 + {
  32 + if (this.isRepeatSubmit(request))
  33 + {
  34 + AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
  35 + ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
  36 + return false;
  37 + }
  38 + }
  39 + return true;
  40 + }
  41 + else
  42 + {
  43 + return super.preHandle(request, response, handler);
  44 + }
  45 + }
  46 +
  47 + /**
  48 + * 验证是否重复提交由子类实现具体的防重复提交的规则
  49 + *
  50 + * @param httpServletRequest
  51 + * @return
  52 + * @throws Exception
  53 + */
  54 + public abstract boolean isRepeatSubmit(HttpServletRequest request);
  55 +}
  1 +package com.ruoyi.framework.interceptor.annotation;
  2 +
  3 +import java.lang.annotation.Documented;
  4 +import java.lang.annotation.ElementType;
  5 +import java.lang.annotation.Inherited;
  6 +import java.lang.annotation.Retention;
  7 +import java.lang.annotation.RetentionPolicy;
  8 +import java.lang.annotation.Target;
  9 +
  10 +/**
  11 + * 自定义注解防止表单重复提交
  12 + *
  13 + * @author ruoyi
  14 + *
  15 + */
  16 +@Inherited
  17 +@Target(ElementType.METHOD)
  18 +@Retention(RetentionPolicy.RUNTIME)
  19 +@Documented
  20 +public @interface RepeatSubmit
  21 +{
  22 +
  23 +}
  1 +package com.ruoyi.framework.interceptor.impl;
  2 +
  3 +import java.util.HashMap;
  4 +import java.util.Map;
  5 +import javax.servlet.http.HttpServletRequest;
  6 +import javax.servlet.http.HttpSession;
  7 +import org.springframework.stereotype.Component;
  8 +import com.alibaba.fastjson.JSONObject;
  9 +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
  10 +
  11 +/**
  12 + * 判断请求url和数据是否和上一次相同,
  13 + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
  14 + *
  15 + * @author ruoyi
  16 + */
  17 +@Component
  18 +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
  19 +{
  20 + public final String REPEAT_PARAMS = "repeatParams";
  21 +
  22 + public final String REPEAT_TIME = "repeatTime";
  23 +
  24 + public final String SESSION_REPEAT_KEY = "repeatData";
  25 +
  26 + /**
  27 + * 间隔时间,单位:秒 默认10秒
  28 + *
  29 + * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
  30 + */
  31 + private int intervalTime = 10;
  32 +
  33 + public void setIntervalTime(int intervalTime)
  34 + {
  35 + this.intervalTime = intervalTime;
  36 + }
  37 +
  38 + @SuppressWarnings("unchecked")
  39 + @Override
  40 + public boolean isRepeatSubmit(HttpServletRequest request)
  41 + {
  42 + // 本次参数及系统时间
  43 + String nowParams = JSONObject.toJSONString(request.getParameterMap());
  44 + Map<String, Object> nowDataMap = new HashMap<String, Object>();
  45 + nowDataMap.put(REPEAT_PARAMS, nowParams);
  46 + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
  47 +
  48 + // 请求地址(作为存放session的key值)
  49 + String url = request.getRequestURI();
  50 +
  51 + HttpSession session = request.getSession();
  52 + Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);
  53 + if (sessionObj != null)
  54 + {
  55 + Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
  56 + if (sessionMap.containsKey(url))
  57 + {
  58 + Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
  59 + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
  60 + {
  61 + return true;
  62 + }
  63 + }
  64 + }
  65 + Map<String, Object> sessionMap = new HashMap<String, Object>();
  66 + sessionMap.put(url, nowDataMap);
  67 + session.setAttribute(SESSION_REPEAT_KEY, sessionMap);
  68 + return false;
  69 + }
  70 +
  71 + /**
  72 + * 判断参数是否相同
  73 + */
  74 + private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
  75 + {
  76 + String nowParams = (String) nowMap.get(REPEAT_PARAMS);
  77 + String preParams = (String) preMap.get(REPEAT_PARAMS);
  78 + return nowParams.equals(preParams);
  79 + }
  80 +
  81 + /**
  82 + * 判断两次间隔时间
  83 + */
  84 + private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
  85 + {
  86 + long time1 = (Long) nowMap.get(REPEAT_TIME);
  87 + long time2 = (Long) preMap.get(REPEAT_TIME);
  88 + if ((time1 - time2) < (this.intervalTime * 1000))
  89 + {
  90 + return true;
  91 + }
  92 + return false;
  93 + }
  94 +}