From ab992f4848d663b0ca5dc65883b05ad950eae778 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Fri, 13 Mar 2020 21:15:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=98=B2=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=B3=A8=E8=A7=A3=E6=97=A0=E6=95=88=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/common/filter/RepeatableFilter.java | 48 +++++++++++ .../filter/RepeatedlyRequestWrapper.java | 84 +++++++++++++++++++ .../common/{xss => filter}/XssFilter.java | 2 +- .../XssHttpServletRequestWrapper.java | 2 +- .../ruoyi/framework/config/FilterConfig.java | 18 +++- .../impl/SameUrlDataInterceptor.java | 31 +++++-- 6 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 ruoyi/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java create mode 100644 ruoyi/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java rename ruoyi/src/main/java/com/ruoyi/common/{xss => filter}/XssFilter.java (95%) rename ruoyi/src/main/java/com/ruoyi/common/{xss => filter}/XssHttpServletRequestWrapper.java (95%) diff --git a/ruoyi/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java new file mode 100644 index 000000000..c33481418 --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -0,0 +1,48 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import com.ruoyi.common.enums.HttpMethod; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + if (HttpMethod.PUT.name().equals(req.getMethod()) || HttpMethod.POST.name().equals(req.getMethod())) + { + RepeatedlyRequestWrapper repeatedlyRequest = new RepeatedlyRequestWrapper((HttpServletRequest) request); + chain.doFilter(repeatedlyRequest, response); + } + else + { + chain.doFilter(request, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 000000000..f059d375c --- /dev/null +++ b/ruoyi/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,84 @@ +package com.ruoyi.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import com.ruoyi.common.utils.StringUtils; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request) throws IOException + { + super(request); + body = readBytes(request.getReader(), "utf-8"); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener listener) + { + + } + + @Override + public int read() throws IOException + { + return bais.read(); + } + }; + } + + /** + * 通过BufferedReader和字符编码集转换成byte数组 + */ + private byte[] readBytes(BufferedReader br, String encoding) throws IOException + { + String str = null, retStr = ""; + while ((str = br.readLine()) != null) + { + retStr += str; + } + if (StringUtils.isNotBlank(retStr)) + { + return retStr.getBytes(Charset.forName(encoding)); + } + return null; + } +} diff --git a/ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java b/ruoyi/src/main/java/com/ruoyi/common/filter/XssFilter.java similarity index 95% rename from ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java rename to ruoyi/src/main/java/com/ruoyi/common/filter/XssFilter.java index d307fbc9f..14954125c 100644 --- a/ruoyi/src/main/java/com/ruoyi/common/xss/XssFilter.java +++ b/ruoyi/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -1,4 +1,4 @@ -package com.ruoyi.common.xss; +package com.ruoyi.common.filter; import java.io.IOException; import java.util.ArrayList; diff --git a/ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java b/ruoyi/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java similarity index 95% rename from ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java rename to ruoyi/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java index 1b90c886d..12ef5517c 100644 --- a/ruoyi/src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java +++ b/ruoyi/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -1,4 +1,4 @@ -package com.ruoyi.common.xss; +package com.ruoyi.common.filter; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java index 92d010e80..8b1783424 100644 --- a/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java +++ b/ruoyi/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -7,8 +7,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.xss.XssFilter; /** * Filter配置 @@ -36,11 +37,24 @@ public class FilterConfig registration.setFilter(new XssFilter()); registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); registration.setName("xssFilter"); - registration.setOrder(Integer.MAX_VALUE); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); Map initParameters = new HashMap(); initParameters.put("excludes", excludes); initParameters.put("enabled", enabled); registration.setInitParameters(initParameters); return registration; } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + } diff --git a/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java index 9d915c6e3..2c3e14ebf 100644 --- a/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java +++ b/ruoyi/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -2,14 +2,19 @@ package com.ruoyi.framework.interceptor.impl; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; +import com.ruoyi.framework.redis.RedisCache; /** - * 判断请求url和数据是否和上一次相同, + * 判断请求url和数据是否和上一次相同, * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 * * @author ruoyi @@ -23,6 +28,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor public final String SESSION_REPEAT_KEY = "repeatData"; + @Autowired + private RedisCache redisCache; + /** * 间隔时间,单位:秒 默认10秒 * @@ -39,8 +47,14 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor @Override public boolean isRepeatSubmit(HttpServletRequest request) { - // 本次参数及系统时间 - String nowParams = JSONObject.toJSONString(request.getParameterMap()); + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + String nowParams = HttpHelper.getBodyString(repeatedlyRequest); + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSONObject.toJSONString(request.getParameterMap()); + } Map nowDataMap = new HashMap(); nowDataMap.put(REPEAT_PARAMS, nowParams); nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); @@ -48,8 +62,7 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor // 请求地址(作为存放session的key值) String url = request.getRequestURI(); - HttpSession session = request.getSession(); - Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY); + Object sessionObj = redisCache.getCacheObject(SESSION_REPEAT_KEY); if (sessionObj != null) { Map sessionMap = (Map) sessionObj; @@ -62,9 +75,9 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor } } } - Map sessionMap = new HashMap(); - sessionMap.put(url, nowDataMap); - session.setAttribute(SESSION_REPEAT_KEY, sessionMap); + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(SESSION_REPEAT_KEY, cacheMap, intervalTime, TimeUnit.SECONDS); return false; }