过滤器和拦截器
前言
Github:https://github.com/HealerJean
1、过滤器和拦截器
1.1、过滤器、拦截器、aop
区别
功能 | 过滤器 | 拦截器 | AOP |
---|---|---|---|
概念 | 是 Java Servlet 规范中的一部分,是在请求进入 Servlet 之前或响应离开 Servlet 之后对请求和响应进行预处理和后处理的组件。它可以对请求的内容、字符编码、请求参数等进行过滤和转换,也可以对响应的内容、状态码等进行修改。 |
是在 Web 框架(如 Spring MVC )中用于对请求进行拦截和处理的组件。它可以在请求到达控制器之前或控制器处理完请求之后执行一些额外的操作,如权限验证、日志记录、性能监控等。 |
一种编程范式,它允许开发者在不修改原有业务逻辑的基础上,对程序进行增强。AOP 通过将横切关注点(如日志记录、事务管理、权限验证等)从业务逻辑中分离出来,形成独立的切面(Aspect),然后在运行时将这些切面织入到目标对象的方法中。 |
应用场景 | 常用于字符编码过滤、敏感词汇过滤、请求合法性检查等场景。 | 常用于权限验证、日志记录、性能监控、请求参数预处理等场景。 | 常用于事务管理、日志记录、权限验证、缓存管理等场景。 |
执行顺序 | 请求进入 Servlet 容器时最先执行,在响应离开 Servlet 容器时最后执行。 |
在过滤器之后,请求进入控制器之前执行,在控制器处理完请求之后,响应返回之前执行。 | 在拦截器之后,目标方法执行前后执行。 |
使用范围 | Servlet 规范的一部分,只能用于基于 Servlet 的 Web 应用。 |
是 Web 框架提供的功能,只能用于特定的 Web 框架,如 Spring MVC 。 |
是一种通用的编程范式,可以用于各种类型的应用,不仅限于 Web 应用。 |
实现方式 | 需要实现 javax.servlet.Filter 接口,并重写 doFilter 方法。 |
在 Spring MVC 中,需要实现 HandlerInterceptor 接口,并重写 preHandle 、postHandle 和 afterCompletion 方法。 |
Spring 框架中,可以使用 XML 配置或注解(如 @Aspect 、@Before 、@After 等)来定义切面和通知。 |
过滤器和拦截器的拦截对象
- 过滤器:对请求和响应进行拦截和处理,可基于
URL
、请求方法、请求头、请求参数等规则进行过滤。例如可以对所有请求进行字符编码过滤。 - 拦截器:在
Web
框架中拦截请求,通常基于URL
匹配规则来决定是否拦截请求。比如在Spring
MVC
里,可以配置拦截器对特定 URL 模式的请求进行拦截。
过滤器和拦截器的区别
- 实现机制
- 拦截器是基于
Java
的反射机制实现的。例如在Spring
MVC
中,拦截器通过反射调用控制器方法前后执行额外逻辑。 - 过滤器是基于函数回调实现的。当请求进入
Servlet
容器时,会回调过滤器的doFilter
方法。
- 拦截器是基于
- 依赖关系
- 拦截器不依赖于
Servlet
容器,它是Web
框架层面的组件。像Spring
MVC
的拦截器可以独立于Servlet
容器进行逻辑处理。 - 过滤器依赖于
Servlet
容器,它是Servlet
规范的一部分,只能在Servlet
容器中使用。
- 拦截器不依赖于
- 作用范围
- 拦截器主要对控制器的请求起作用,在现代
Web
框架中可以对各种形式的控制器请求进行拦截处理。 - 过滤器可以对几乎所有的请求起作用,包括静态资源请求等。
- 拦截器主要对控制器的请求起作用,在现代
- 访问对象
- 拦截器可以访问
action
上下文、值栈里的对象(在如Struts
等框架中),在Spring
MVC
里能访问请求、响应、控制器方法等相关信息。 - 过滤器不能直接访问
action
上下文、值栈里的对象,它主要操作请求和响应对象。
- 拦截器可以访问
- 调用时机和频率
- 拦截器在一次请求处理过程中,
preHandle
、postHandle
和afterCompletion
方法会按顺序执行,针对每次请求都会进行相应操作。 - 过滤器每次请求和响应经过时都会执行
doFilter
方法,而不是只在容器初始化时执行一次。
- 拦截器在一次请求处理过程中,
- 对
IOC
容器的访问- 拦截器可以获取
IOC
容器中的各个bean
,能在拦截器里注入一个service
来调用业务逻辑。例如在Spring
MVC
拦截器中可以注入Service
层的Bean
。 - 过滤器通常不能直接获取
IOC
容器中的bean
,不过可以通过一些方式(如在 ServletContext 中获取 Spring 上下文)间接获取。
- 拦截器可以获取
1.2、忽略URL配置
1.2.1、过滤器
Spring
中自定义过滤器(Filter
)一般只有一个方法,返回值是void
,当请求到达web
容器时,会探测当前请求地址是否配置有过滤器,有则调用该过滤器的方法(可能会有多个过滤器),然后才调用真实的业务逻辑,至此过滤器任务完成。过滤器并没有定义业务逻辑执行前、后等,仅仅是请求到达就执行。特别注意:过滤器方法的入参有
request
,response
,FilterChain
,其中FilterChain
是过滤器链,使用比较简单,而request
,response
则关联到请求流程,因此可以对请求参数做过滤和修改,同时FilterChain过滤链执行完,并且完成业务流程后,会返回到过滤器,此时也可以对请求的返回数据做处理。
public class Filter_1 implements Filter {
private FilterConfig filterConfig;
private List<String> whiteList = Arrays.asList("/api/business/contract/notifyAsync","/api/account/notify");
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
if (whiteList.contains(((HttpServletRequest) servletRequest).getRequestURI())) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//添加url进行判断忽略下面的方法
SpaceHttpServletRequestWrapper requestWrapper = new SpaceHttpServletRequestWrapper(
(HttpServletRequest) servletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
}
1.2.2、拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor_1)
.addPathPatterns("/**")
.excludePathPatterns("/develop/swagger/**");//忽略的
}
1.3、启动顺序
⬤ 谁先配置,谁先启动,DispatcherServlet之后那就顺序反过来执行了,具体看测试结果
⬤ order(2), 数字越小,越先执行
1.3、过滤器和拦截器
1、
preHandle
返 回false
,postHandle
不会执行,但afterCompletion
会执行2、拦截器链是按照配置的顺序依次执行的,当一个拦截器的
preHandle
方法返回false
时,后续拦截器的preHandle
方法不会再执行,并且已经执行过preHandle
方法且返回true
的拦截器的postHandle
方法不会执行,但它们的afterCompletion
方法会执行。
1. postHandle
方法
postHandle
方法是在处理器处理请求之后、视图渲染之前被调用。当拦截器链中某个拦截器的 preHandle
方法返回 false
时,请求处理流程会中断,后续的处理器不会被调用,也就不会进入到视图渲染阶段,所以已经执行过 preHandle
方法且返回 true
的拦截器的 postHandle
方法不会执行。
2. afterCompletion
方法
afterCompletion
方法是在整个请求处理完成后(包括视图渲染完成)被调用,主要用于资源清理等操作。无论请求处理是否正常完成,已经执行过 preHandle
方法且返回 true
的拦截器的 afterCompletion
方法都会执行。
1.3.1、过滤器1
package com.hlj.proj.config.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParamsFilter
* @Date 2019/9/29 14:33.
* @Description
*/
public class Filter_1 implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
System.out.println("############ Filter_1 在DispatcherServlet之前执行############");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("############ Filter_1 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############");
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
1.3.2、过滤器2
package com.hlj.proj.config.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParamsFilter
* @Date 2019/9/29 14:33.
* @Description 空格过滤
*/
public class Filter_2 implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
System.out.println("############ Filter_2 在DispatcherServlet之前执行############");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("############ Filter_2 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############");
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
1.3.3、拦截器1
package com.hlj.proj.config.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author HealerJean
* @ClassName Interceptor_1
* @date 2019/11/21 22:08.
* @Description
*/
@Component
@Slf4j
public class Interceptor_1 implements HandlerInterceptor {
/**
* 在DispatcherServlet之前执行
* */
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("************ Interceptor_1 在DispatcherServlet之前执行**********");
return true;
}
/**
* 在controller执行之后的DispatcherServlet之后执行
* */
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("************ Interceptor_1 在controller执行之后的DispatcherServlet之后执行**********");
}
/**
* 在页面渲染完成返回给客户端之前执行
* */
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("************ Interceptor_1 在页面渲染完成返回给客户端之前执行**********");
}
}
1.3.4、拦截器2
package com.hlj.proj.config.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author HealerJean
* @ClassName Interceptor_1
* @date 2019/11/21 22:08.
* @Description
*/
@Component
@Slf4j
public class Interceptor_2 implements HandlerInterceptor {
/**
* 在DispatcherServlet之前执行
* */
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("************ Interceptor_2 在DispatcherServlet之前执行**********");
return true;
}
/**
* 在controller执行之后的DispatcherServlet之后执行
* */
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("************ Interceptor_2 在controller执行之后的DispatcherServlet之后执行**********");
}
/**
* 在页面渲染完成返回给客户端之前执行
* */
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("************ Interceptor_2 在页面渲染完成返回给客户端之前执行**********");
}
}
1.3.7、配置:注意配置顺序
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Resource
private Interceptor_1 interceptor_1;
@Resource
private Interceptor_2 interceptor_2;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor_1)
.addPathPatterns("/**")
.excludePathPatterns("/develop/swagger/**");
registry.addInterceptor(interceptor_2)
.addPathPatterns("/**")
.excludePathPatterns("/develop/swagger/**");
}
@Bean
public FilterRegistrationBean filter1() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new Filter_1());
fitler.addUrlPatterns("/hlj/*");
fitler.setName("filter_1");
fitler.setDispatcherTypes(DispatcherType.REQUEST);
return fitler;
}
@Bean
public FilterRegistrationBean filter2() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new Filter_2());
fitler.addUrlPatterns("/hlj/*");
fitler.setName("filter_2");
fitler.setDispatcherTypes(DispatcherType.REQUEST);
return fitler;
}
}
1.3.6、测试启动
############ Filter_1 在DispatcherServlet之前执行############
############ Filter_2 在DispatcherServlet之前执行############
************ Interceptor_1 在DispatcherServlet之前执行**********
************ Interceptor_2 在DispatcherServlet之前执行**********
样例--------GET请求------请求参数{} com.hlj.proj.controller.DemoController.get[58]
************ Interceptor_2 在controller执行之后的DispatcherServlet之后执行**********
************ Interceptor_1 在controller执行之后的DispatcherServlet之后执行**********
************ Interceptor_2 在页面渲染完成返回给客户端之前执行**********
************ Interceptor_1 在页面渲染完成返回给客户端之前执行**********
############ Filter_2 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############
############ Filter_1 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后############
谁配置在前,谁先执行,然后反过来
2、实用过滤器
主要是重写HttpServletRequestWrapper
// 404是不会进入的
// 重写getParameterValues方法,通过循环取出每一个请求结果,再对请求结果进行过滤
@Override
public String[] getParameterValues(String parameter) {
}
// 404是不会进入的
// 重写getParameter方法 对请求结果进行过滤
@Override
public String getParameter(String name) {
}
2.1、空格过滤器
2.1.1、普通请求的空格过滤器
2.1.1.1、SpaceParamsFilter
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParamsFilter
* @Date 2019/9/29 14:33.
* @Description 普通请求空格过滤
*/
public class SpaceParamsFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
filterChain.doFilter(new SpaceParameHttpServletRequestWrapper(
(HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
2.11.2、SpaceParameHttpServletRequestWrapper
package com.hlj.proj.config.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParameHttpServletRequestWrapper
* @Date 2019/9/29 14:44.
* 空格过滤处理
* 下面的将构造器中将所有的请求的参数获取了(包括404),但是其实并没有进行空格过滤处理),
* 一般普通请求空格过滤用下面的就可以了
*/
public class SpaceParameHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 下面的将构造器中将所有的请求的参数获取了(包括404),但是其实并没有进行空格过滤处理),
* 一般普通请求空格过滤用下面的就可以了
*/
public SpaceParameHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
super(servletRequest);
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return new String[0];
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = values[i].trim();
}
return encodedValues;
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (value == null) {
return null;
}
return value.trim();
}
// private Map<String, String[]> params = new HashMap<>();
//
// public SpaceParameHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
// super(servletRequest);
//
// Map<String, String[]> requestMap = servletRequest.getParameterMap();
// this.params.putAll(requestMap);
// this.modifyParameterValues();
// }
//
// /**
// * 404是不会进入的
// * 重写getParameterValues方法,通过循环取出每一个请求结果,再对请求结果进行过滤
// */
// @Override
// public String[] getParameterValues(String name) {
// return params.get(name);
// }
//
// /**
// * 重写getParameter方法 对请求结果进行过滤
// **/
// @Override
// public String getParameter(String name) {
// String[]values = params.get(name);
// if (values == null || values.length == 0) {
// return null;
// }
// return values[0];
// }
// public void modifyParameterValues() {
// Set<String> set = params.keySet();
// Iterator<String> it = set.iterator();
// while (it.hasNext()) {
// String key = it.next();
// String[] values = params.get(key);
// values[0] = values[0].trim();
// params.put(key, values);
// }
// }
}
2.1.1.3、过滤器配置
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean spaceJsonFilter() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new SpaceJsonFilter());
fitler.addUrlPatterns("/hlj/*");
fitler.setName("SpaceJsonFilter");
fitler.setDispatcherTypes(DispatcherType.REQUEST);
return fitler;
}
}
2.1.2、Json
请求空格过滤
2.1.2.1、SpaceJsonFilter
package com.hlj.proj.config.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParamsFilter
* @Date 2019/9/29 14:33.
* @Description Josn请求空格过滤
*/
public class SpaceJsonFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
filterChain.doFilter(new SpaceJsonHttpServletRequestWrapper(
(HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
2.1.2.2、SpaceJsonHttpServletRequestWrapper
package com.hlj.proj.config.filter;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hlj.proj.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParameHttpServletRequestWrapper
* @Date 2019/9/29 14:44.
* @Description
*/
public class SpaceJsonHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
/**
* 预先出初始化数据
*/
public SpaceJsonHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
super(servletRequest);
BufferedReader reader = servletRequest.getReader();
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
if (StringUtils.isNotBlank(json)) {
json = JsonUtils.toJsonString(trimSpace(json));
}
body = json.getBytes("utf-8");
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 重写getInputStream方法 Json类型的请求参数必须通过流才能获取到值
*/
@Override
public ServletInputStream getInputStream() {
ByteArrayInputStream bis = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return bis.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
public static Map<String, Object> trimSpace(String jsonString) {
Map<String, Object> map = new HashMap<>();
JSONObject jsonObject = JSONObject.parseObject(jsonString);
for (Object k : jsonObject.keySet()) {
Object o = jsonObject.get(k);
if (o instanceof JSONArray) {
List<Object> list = new ArrayList<>();
Iterator<Object> it = ((JSONArray) o).iterator();
while (it.hasNext()) {
Object obj = it.next();
if (obj instanceof JSONObject) {
list.add(trimSpace(obj.toString()));
} else {
list.add(obj.toString().trim());
}
}
map.put(k.toString(), list);
} else if (o instanceof JSONObject) {
// 如果内层是json对象的话,继续解析
map.put(k.toString(), trimSpace(o.toString()));
} else {
// 如果内层是普通对象的话,直接放入map中
map.put(k.toString(), o.toString().trim());
}
}
return map;
}
}
2.1.2.3、过滤器配置
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean spaceFilter() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new SpaceFilter());
fitler.addUrlPatterns("/*");
fitler.setName("SpaceFilter");
fitler.setDispatcherTypes(DispatcherType.REQUEST);
return fitler;
}
}
2.1.3、结合上面二者
2.1.3.1、SpaceFilter
package com.hlj.proj.config.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParamsFilter
* @Date 2019/9/29 14:33.
* @Description 空格过滤
*/
public class SpaceFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
filterChain.doFilter(new SpaceHttpServletRequestWrapper(
(HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
2.1.3.2、SpaceHttpServletRequestWrapper
package com.hlj.proj.config.filter;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hlj.proj.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
/**
* @author HealerJean
* @version 1.0v
* @ClassName SpaceParameHttpServletRequestWrapper
* @Date 2019/9/29 14:44.
* @Description
*/
public class SpaceHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
/**
* 预先出初始化数据
*/
public SpaceHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
super(servletRequest);
BufferedReader reader = servletRequest.getReader();
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
if (StringUtils.isNotBlank(json)) {
json = JsonUtils.toJsonString(trimSpace(json));
}
body = json.getBytes("utf-8");
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return new String[0];
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = values[i].trim();
}
return encodedValues;
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (value == null) {
return null;
}
return value.trim();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 重写getInputStream方法 Json类型的请求参数必须通过流才能获取到值
*/
@Override
public ServletInputStream getInputStream() {
ByteArrayInputStream bis = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return bis.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
public static Map<String, Object> trimSpace(String jsonString) {
Map<String, Object> map = new HashMap<>();
JSONObject jsonObject = JSONObject.parseObject(jsonString);
for (Object k : jsonObject.keySet()) {
Object o = jsonObject.get(k);
if (o instanceof JSONArray) {
List<Object> list = new ArrayList<>();
Iterator<Object> it = ((JSONArray) o).iterator();
while (it.hasNext()) {
Object obj = it.next();
if (obj instanceof JSONObject) {
list.add(trimSpace(obj.toString()));
} else {
list.add(obj.toString().trim());
}
}
map.put(k.toString(), list);
} else if (o instanceof JSONObject) {
// 如果内层是json对象的话,继续解析
map.put(k.toString(), trimSpace(o.toString()));
} else {
// 如果内层是普通对象的话,直接放入map中
map.put(k.toString(), o.toString().trim());
}
}
return map;
}
}
2.1.3.3、配置过滤器
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean spaceJsonFilter() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new SpaceFilter());
fitler.addUrlPatterns("/hlj/*");
fitler.setName("SpaceJsonFilter");
fitler.setDispatcherTypes(DispatcherType.REQUEST);
return fitler;
}
}
2.2、Xss
攻击过滤器
2.2.1、XssFilter
package com.hlj.proj.config.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author HealerJean
* @ClassName XssFilter
* @Date 2019/11/21 17:38.
* @Description 防Xss攻击Filter ,只能阻止普通的http请求,不能阻止Json请求,如果需要组织Json请求则需要按照空格过滤器一样进行配置
*/
public class XssFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
IOException, ServletException {
filterChain.doFilter(new XssHttpServletRequestWrapper(
(HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
2.2.2、SpaceParameHttpServletRequestWrapper
package com.hlj.proj.config.filter;
import org.apache.commons.lang3.StringEscapeUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
/**
* 只有确定的controller才回访问这里
*/
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return new String[0];
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
private String cleanXSS(String value) {
return StringEscapeUtils.escapeXml10(value);
}
}
2.2.3、配置过滤器
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean xssSqlFilter() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new XssFilter());
fitler.addUrlPatterns("/hlj/*");
fitler.setName("XssFilter");
fitler.setDispatcherTypes(DispatcherType.REQUEST);
return fitler;
}
}
3、实用拦截器
3.1、打印访问日志
@Component
@Slf4j
public class UrlInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
log.info("admin:请求地址:[{}];访问ip:[{}]", httpServletRequest.getRequestURL(), IpUtil.getIp());
return true;
}
}
3.2、自定义注解方法请求次数限制
3.2.1、设置默认方法允许进入的次数
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EntryTimes {
/**
* 方法允许进入的次数
* @return
*/
int value() default 1;
/**
* 可以的前缀 url前缀
* @return
*/
String prefix() default "";
}
3.2.2、aop拦截
import com.duodian.admore.zhaobutong.annotation.EntryTimes;
import com.duodian.admore.zhaobutong.bean.Code;
import com.duodian.admore.zhaobutong.bean.Response;
import com.duodian.admore.zhaobutong.util.AesUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* 控制每个用户访问Controller方法的次数
* Created by fengchuanbo on 2017/5/25.
*/
@Aspect
@Component
public class MethodEntryTimesLimitInterceptor {
private static final String METHOD_CAN_ENTRY_TIMES_KEY = "method:entry:times:";
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 需要有 EntryTimes 标注,并且第一个参数需要是 AuthUser才可以
* @param pjp
* @return
* @throws Throwable
*/
@Around("@annotation(com.duodian.admore.zhaobutong.annotation.EntryTimes)")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getParameter("token");
String aes = AesUtils.LoginDecrypt(token);
Long userId = Long.valueOf(aes.split("#")[0]);
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
EntryTimes annotation = method.getAnnotation(EntryTimes.class);
int times = annotation.value();
String key = METHOD_CAN_ENTRY_TIMES_KEY + ":" + annotation.prefix() + ":" + userId;
// 没有整个方法使用一个redis链接,是为了方式方法执行时间过长一直占用redis链接。
Long increment = getEntryTimes(key);
Object retVal;
try {
// 放在try里面,才能执行finally
if (increment > times){
// 设置十秒钟超时,防止finally在系统崩溃或重启时没有执行造成用户不能操作。
expireKey(key,10);
return Response.of(Code.ACTION_FREQUENT);
}
//调用核心逻辑
retVal = pjp.proceed();
}finally {
deleteKey(key);
}
return retVal;
}
private Long getEntryTimes(String key){
return stringRedisTemplate.opsForValue().increment(key,1);
}
private void deleteKey(String key){
stringRedisTemplate.delete(key);
}
//添加缓存紧急数据的增加
private void expireKey(String key, int seconds){
stringRedisTemplate.boundValueOps(key).expire(seconds, TimeUnit.SECONDS);
}
}
//或者可以参考 opt项目
public Long increase(final String key,final Long seconds){
return redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
redisConnection.select(DB_INDEX);
byte[] keyBytes = stringRedisSerializer.serialize(key);
Long val = redisConnection.incr(keyBytes);
redisConnection.expire(keyBytes,seconds);
return val;
}
});
}
public String getRunStatus(){
return redisTemplate.execute(new RedisCallback<String>() {
public String doInRedis(RedisConnection redisConnection) throws DataAccessException {
redisConnection.select(DB_INDEX);
byte[] result = redisConnection.get(stringRedisSerializer.serialize(KEY));
return stringRedisSerializer.deserialize(result);
}
});
}