作者 RuoYi

修复防重复提交注解无效问题

  1 +package com.ruoyi.common.filter;
  2 +
  3 +import java.io.IOException;
  4 +import javax.servlet.Filter;
  5 +import javax.servlet.FilterChain;
  6 +import javax.servlet.FilterConfig;
  7 +import javax.servlet.ServletException;
  8 +import javax.servlet.ServletRequest;
  9 +import javax.servlet.ServletResponse;
  10 +import javax.servlet.http.HttpServletRequest;
  11 +
  12 +import com.ruoyi.common.enums.HttpMethod;
  13 +
  14 +/**
  15 + * Repeatable 过滤器
  16 + *
  17 + * @author ruoyi
  18 + */
  19 +public class RepeatableFilter implements Filter
  20 +{
  21 + @Override
  22 + public void init(FilterConfig filterConfig) throws ServletException
  23 + {
  24 +
  25 + }
  26 +
  27 + @Override
  28 + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  29 + throws IOException, ServletException
  30 + {
  31 + HttpServletRequest req = (HttpServletRequest) request;
  32 + if (HttpMethod.PUT.name().equals(req.getMethod()) || HttpMethod.POST.name().equals(req.getMethod()))
  33 + {
  34 + RepeatedlyRequestWrapper repeatedlyRequest = new RepeatedlyRequestWrapper((HttpServletRequest) request);
  35 + chain.doFilter(repeatedlyRequest, response);
  36 + }
  37 + else
  38 + {
  39 + chain.doFilter(request, response);
  40 + }
  41 + }
  42 +
  43 + @Override
  44 + public void destroy()
  45 + {
  46 +
  47 + }
  48 +}
  1 +package com.ruoyi.common.filter;
  2 +
  3 +import java.io.BufferedReader;
  4 +import java.io.ByteArrayInputStream;
  5 +import java.io.IOException;
  6 +import java.io.InputStreamReader;
  7 +import java.nio.charset.Charset;
  8 +import javax.servlet.ReadListener;
  9 +import javax.servlet.ServletInputStream;
  10 +import javax.servlet.http.HttpServletRequest;
  11 +import javax.servlet.http.HttpServletRequestWrapper;
  12 +import com.ruoyi.common.utils.StringUtils;
  13 +
  14 +/**
  15 + * 构建可重复读取inputStream的request
  16 + *
  17 + * @author ruoyi
  18 + */
  19 +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
  20 +{
  21 + private final byte[] body;
  22 +
  23 + public RepeatedlyRequestWrapper(HttpServletRequest request) throws IOException
  24 + {
  25 + super(request);
  26 + body = readBytes(request.getReader(), "utf-8");
  27 + }
  28 +
  29 + @Override
  30 + public BufferedReader getReader() throws IOException
  31 + {
  32 + return new BufferedReader(new InputStreamReader(getInputStream()));
  33 + }
  34 +
  35 + @Override
  36 + public ServletInputStream getInputStream() throws IOException
  37 + {
  38 + final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  39 + return new ServletInputStream()
  40 + {
  41 +
  42 + @Override
  43 + public boolean isFinished()
  44 + {
  45 + return false;
  46 + }
  47 +
  48 + @Override
  49 + public boolean isReady()
  50 + {
  51 + return false;
  52 + }
  53 +
  54 + @Override
  55 + public void setReadListener(ReadListener listener)
  56 + {
  57 +
  58 + }
  59 +
  60 + @Override
  61 + public int read() throws IOException
  62 + {
  63 + return bais.read();
  64 + }
  65 + };
  66 + }
  67 +
  68 + /**
  69 + * 通过BufferedReader和字符编码集转换成byte数组
  70 + */
  71 + private byte[] readBytes(BufferedReader br, String encoding) throws IOException
  72 + {
  73 + String str = null, retStr = "";
  74 + while ((str = br.readLine()) != null)
  75 + {
  76 + retStr += str;
  77 + }
  78 + if (StringUtils.isNotBlank(retStr))
  79 + {
  80 + return retStr.getBytes(Charset.forName(encoding));
  81 + }
  82 + return null;
  83 + }
  84 +}
1 -package com.ruoyi.common.xss; 1 +package com.ruoyi.common.filter;
2 2
3 import java.io.IOException; 3 import java.io.IOException;
4 import java.util.ArrayList; 4 import java.util.ArrayList;
1 -package com.ruoyi.common.xss; 1 +package com.ruoyi.common.filter;
2 2
3 import java.io.ByteArrayInputStream; 3 import java.io.ByteArrayInputStream;
4 import java.io.IOException; 4 import java.io.IOException;
@@ -7,8 +7,9 @@ import org.springframework.beans.factory.annotation.Value; @@ -7,8 +7,9 @@ import org.springframework.beans.factory.annotation.Value;
7 import org.springframework.boot.web.servlet.FilterRegistrationBean; 7 import org.springframework.boot.web.servlet.FilterRegistrationBean;
8 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
9 import org.springframework.context.annotation.Configuration; 9 import org.springframework.context.annotation.Configuration;
  10 +import com.ruoyi.common.filter.RepeatableFilter;
  11 +import com.ruoyi.common.filter.XssFilter;
10 import com.ruoyi.common.utils.StringUtils; 12 import com.ruoyi.common.utils.StringUtils;
11 -import com.ruoyi.common.xss.XssFilter;  
12 13
13 /** 14 /**
14 * Filter配置 15 * Filter配置
@@ -36,11 +37,24 @@ public class FilterConfig @@ -36,11 +37,24 @@ public class FilterConfig
36 registration.setFilter(new XssFilter()); 37 registration.setFilter(new XssFilter());
37 registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); 38 registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
38 registration.setName("xssFilter"); 39 registration.setName("xssFilter");
39 - registration.setOrder(Integer.MAX_VALUE); 40 + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
40 Map<String, String> initParameters = new HashMap<String, String>(); 41 Map<String, String> initParameters = new HashMap<String, String>();
41 initParameters.put("excludes", excludes); 42 initParameters.put("excludes", excludes);
42 initParameters.put("enabled", enabled); 43 initParameters.put("enabled", enabled);
43 registration.setInitParameters(initParameters); 44 registration.setInitParameters(initParameters);
44 return registration; 45 return registration;
45 } 46 }
  47 +
  48 + @SuppressWarnings({ "rawtypes", "unchecked" })
  49 + @Bean
  50 + public FilterRegistrationBean someFilterRegistration()
  51 + {
  52 + FilterRegistrationBean registration = new FilterRegistrationBean();
  53 + registration.setFilter(new RepeatableFilter());
  54 + registration.addUrlPatterns("/*");
  55 + registration.setName("repeatableFilter");
  56 + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
  57 + return registration;
  58 + }
  59 +
46 } 60 }
@@ -2,14 +2,19 @@ package com.ruoyi.framework.interceptor.impl; @@ -2,14 +2,19 @@ package com.ruoyi.framework.interceptor.impl;
2 2
3 import java.util.HashMap; 3 import java.util.HashMap;
4 import java.util.Map; 4 import java.util.Map;
  5 +import java.util.concurrent.TimeUnit;
5 import javax.servlet.http.HttpServletRequest; 6 import javax.servlet.http.HttpServletRequest;
6 -import javax.servlet.http.HttpSession; 7 +import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.stereotype.Component; 8 import org.springframework.stereotype.Component;
8 import com.alibaba.fastjson.JSONObject; 9 import com.alibaba.fastjson.JSONObject;
  10 +import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
  11 +import com.ruoyi.common.utils.StringUtils;
  12 +import com.ruoyi.common.utils.http.HttpHelper;
9 import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; 13 import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
  14 +import com.ruoyi.framework.redis.RedisCache;
10 15
11 /** 16 /**
12 - * 判断请求url和数据是否和上一次相同, 17 + * 判断请求url和数据是否和上一次相同,
13 * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 18 * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
14 * 19 *
15 * @author ruoyi 20 * @author ruoyi
@@ -23,6 +28,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor @@ -23,6 +28,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
23 28
24 public final String SESSION_REPEAT_KEY = "repeatData"; 29 public final String SESSION_REPEAT_KEY = "repeatData";
25 30
  31 + @Autowired
  32 + private RedisCache redisCache;
  33 +
26 /** 34 /**
27 * 间隔时间,单位:秒 默认10秒 35 * 间隔时间,单位:秒 默认10秒
28 * 36 *
@@ -39,8 +47,14 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor @@ -39,8 +47,14 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
39 @Override 47 @Override
40 public boolean isRepeatSubmit(HttpServletRequest request) 48 public boolean isRepeatSubmit(HttpServletRequest request)
41 { 49 {
42 - // 本次参数及系统时间  
43 - String nowParams = JSONObject.toJSONString(request.getParameterMap()); 50 + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
  51 + String nowParams = HttpHelper.getBodyString(repeatedlyRequest);
  52 +
  53 + // body参数为空,获取Parameter的数据
  54 + if (StringUtils.isEmpty(nowParams))
  55 + {
  56 + nowParams = JSONObject.toJSONString(request.getParameterMap());
  57 + }
44 Map<String, Object> nowDataMap = new HashMap<String, Object>(); 58 Map<String, Object> nowDataMap = new HashMap<String, Object>();
45 nowDataMap.put(REPEAT_PARAMS, nowParams); 59 nowDataMap.put(REPEAT_PARAMS, nowParams);
46 nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); 60 nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
@@ -48,8 +62,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor @@ -48,8 +62,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
48 // 请求地址(作为存放session的key值) 62 // 请求地址(作为存放session的key值)
49 String url = request.getRequestURI(); 63 String url = request.getRequestURI();
50 64
51 - HttpSession session = request.getSession();  
52 - Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY); 65 + Object sessionObj = redisCache.getCacheObject(SESSION_REPEAT_KEY);
53 if (sessionObj != null) 66 if (sessionObj != null)
54 { 67 {
55 Map<String, Object> sessionMap = (Map<String, Object>) sessionObj; 68 Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
@@ -62,9 +75,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor @@ -62,9 +75,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
62 } 75 }
63 } 76 }
64 } 77 }
65 - Map<String, Object> sessionMap = new HashMap<String, Object>();  
66 - sessionMap.put(url, nowDataMap);  
67 - session.setAttribute(SESSION_REPEAT_KEY, sessionMap); 78 + Map<String, Object> cacheMap = new HashMap<String, Object>();
  79 + cacheMap.put(url, nowDataMap);
  80 + redisCache.setCacheObject(SESSION_REPEAT_KEY, cacheMap, intervalTime, TimeUnit.SECONDS);
68 return false; 81 return false;
69 } 82 }
70 83