一、过滤器
1. 定义方式
过滤器是 Servlet
规范中的一种组件,它可以在请求被处理之前或之后对请求和响应进行预处理和后处理,可以用于实现一些通用的功能,例如:请求编码转换、登录验证、权限控制、性能监控等。
过滤器可以通过注解方式或者 bean
实例注入的方式创建,但不管哪种方式都需要自定义创建过滤器实现类。
新建自定义过滤器类并实现 Filter
类,过滤器的核心业务逻辑即在 doFilter()
方法中。
public class MyFilter1 implements Filter {
private final Logger LOGGER = LoggerFactory.getLogger(MyFilter1.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
/**
* 过滤器业务逻辑
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
LOGGER.info("filter-1 doFilter.");
HttpServletRequest req = (HttpServletRequest) servletRequest;
// 打印请求参数信息
Map<String, String> parameterMap = new HashMap<>();
req.getParameterMap().keySet().forEach(it -> {
parameterMap.put(it, req.getParameter(it));
});
LOGGER.info("filter-1: url=【{}】, params=【{}】", req.getRequestURI(), parameterMap);
// 放行请求
filterChain.doFilter(req, servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
2. 注入方式
(1) 注解实现
通过注解实现即在自定义过滤器类上添加 @WebFilter
并在启动类上添加 @ServletComponentScan
即可。
若同时创建了多个过滤器,可通过 @Order
注解设置优先值,值越高优先级则越高。
@Order(1)
@WebFilter
public class MyFilter1 implements Filter {
// 略去实现逻辑
}
@ServletComponentScan
@SpringBootApplication
public class DemoFilterApplication {
public static void main(String[] args) {
SpringApplication.run(DemoFilterApplication.class, args);
}
}
(2) 配置实现
除了通过注解实现另一种方式就是通过注入 bean
对象实现,我更推荐这种方法,配置调整更为方便。
@Configuration
public class FilterConfig {
/**
* 可以获取原始的 http 请求与相应, 但无法获取请求要访问的类与方法以及参数
*/
@Bean
public FilterRegistrationBean<MyFilter1> orderFilter1() {
FilterRegistrationBean<MyFilter1> filter = new FilterRegistrationBean<>();
// 设置名称
filter.setName("filter-1");
// 设置作用路径
filter.setUrlPatterns(Collections.singleton("/api/filter/demo1/*"));
// 指定优先级
filter.setOrder(-1);
// 指定自定义过滤器
filter.setFilter(new MyFilter1());
return filter;
}
}
3. 资源忽略
在上面的示例中通过 setUrlPatterns()
配置了拦截作用域,但拦截器并没有提供提供显式的白名单配置接口。
因此为了只能另辟蹊径实现白名单效果,你可以在过滤器的 doFilter()
中通过 req.getRequestURI()
获取请求的资源实现判断过滤,从而实现白名单效果。
下面介绍另一种方法,自定义过滤器类并继承 OncePerRequestFilter
类,将白名单配置策略配置于 shouldNotFilter()
中。
public class MyFilter2 extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 同 Filter 的 doFilter()
}
/**
* 自定义白名单,返回值 true 则不会触发 doFilter()
*/
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String[] excludes = {"/api/filter2/demo3"};
String path = request.getRequestURI();
return Arrays.asList(excludes).contains(path);
}
}
二、拦截器
拦截器是 Spring
框架中的一个组件,它可以在请求处理前后和异常处理时进行预处理和后处理。拦截器和过滤器类似,但它是 Spring
框架中的一部分,可以更方便地使用 Spring
中的功能。
通过 request
与 response
这两个参数,拦截器可以获取请求的相关信息,如请求 URL
、请求参数等,并可以修改响应的相关信息,如设置响应头、设置响应编码等。
1. 业务定义
通过实现 HandlerInterceptor
接口类即可自定义拦截器功能。
其中方法参数中的 Object
代表当前请求所对应的处理器对象 (Handler)
,它可以是一个 Controller
方法,也可以是一个静态资源等等,具体取决于拦截器的配置和拦截器所拦截的请求路径。
@Component
public class MyInterceptor implements HandlerInterceptor {
/**
* 发送请求之前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre Handle method is Calling");
return true;
}
/**
* 发送请求之后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
/**
* 请求完成之后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
2. 实例装配
完成上述拦截器的定义之后就可以禁停实现装配了,即配置拦截哪些请求。
如下设置中即所有在 /api/
路径下的请求都会触发我们所定义的拦截器,但其中 /api/error
请求则会放行。可根据你的业务需求创建不同的拦截器针对不同的请求进行拦截,这里不作详细介绍。
其中 /*
表示当前路径,不包括子路径,而 /**
表示当前路径以及所有子路径。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
MyInterceptor myInterceptor = new MyInterceptor();
String[] path = {"/api/**"};
String[] excludePath = {"/api/error"};
registry.addInterceptor(myInterceptor)
.addPathPatterns(path)
.excludePathPatterns(excludePath);
}
}
3. 效果测试
新增一个简单的 Controller
接口用于测试拦截效果。
@RestController
@RequestMapping("api/interceptor")
public class StudentController {
@GetMapping("demo1")
public String demo1() {
return "This is demo1.";
}
}
完成上述配置之后启动工程,通过 Postman
请求 /api/interceptor/demo1
接口,即可看到控制台打印拦截器中配置的打印信息。
三、作用区别
1. 区别
(1) 过滤器
基于 Servlet
,通过集成 Filter
实现过滤效果,可以获取原始的 HTTP
请求与响应,但无法获取请求要访问的类与方法以及参数。
(2) 拦截器
基于 Spring MVC
提供的拦截器接口自定义实现,可以获取请求的状态和访问的类与方法,但无法获取请求参数的值。
(3) AOP切面
基于 Spring
, 通过 @Aspect
注解实现,可以获取访问的类方法以及参数值,但是无法获取 HTTP
原始的请求与相应的对象。
2. 执行顺序
(1) 请求处理顺序
过滤器
-> 拦截器
-> AOP切面
(2) 报错处理顺序
AOP切面
-> controllerAdvice
-> 拦截器
-> 过滤器