介绍
概念
客户端发送请求(request)至服务端,在request到达servlet之前,过滤器(Filter)可以对请求进行过滤,作预处理;servlet返回响应(response)后,过滤器还可以在response返回给客户端前,对response作处理。
作用
可以实现URL级别的权限控制、黑名单过滤、防范sql注入攻击等。
Filter接口
Servlet API提供了一个Filter接口,编写的过滤器必须实现该接口。
Filter的生命周期
Filter的生命周期即Filter对象从被创建到被销毁的过程。
Filter的生命周期主要体现在以下方法中:
- 构造方法
- 服务器一启动就被执行,说明服务器一启动就创建Filter对象
- 在整个生命周期过程中只执行一次,说明Filter是单例的
- init()方法
public voic init(FilterConfig filterConfig) throws ServletException {}
- 服务器一启动就执行,对过滤器做一些初始化的操作
- 在整个生命周期过程中只执行一次
- 该方法中有一个参数:FilterConfig filterConfig
- doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException.ServletException {}
- 在请求被拦截的资源时被调用,在这个方法中可以对request和response做处理
- 在整个生命周期过程中可以被调用多次,每次请求被拦截时都会调用
- 该方法中有三个参数:ServletRequest request, ServletResponse response, FilterChain chain
- request和response和Servlet中的作用一样
- chain用来放行请求
- destroy()方法
public void destroy() {}
- 在服务器关闭时执行
- 在整个生命周期过程中只被执行一次
FilterConfig
与普通的Servlet程序一样,Filter程序也很可能需要访问Servlet容器。Servlet规范将代表ServletContext对象和Filter的配置参数信息都封装到一个称为FilterConfig的对象中。
FilterConfig接口则用于定义FilterConfig对象应该对外提供的方法,以便在Filter程序中可以调用这些方法来获取ServletContext对象,以及获取在web.xml文件中为Filter设置的fillter名称和初始化参数。
- FilterConfig接口定义的各个方法:
String getFilterName()
:获取Filter的名称。即web.xml文件中过滤器的<filter-name>
元素的设置值。String getInitParameter(String name)
: 返回在web.xml文件中为Filter所设置的某个名称的初始化的参数值。如果不存在返回null.Enumeration getInitParameterNames()
:返回过滤器的所有初始化参数的名字的枚举集合。public ServletContext getServletContext()
:返回Servlet上下文对象的引用。
FilterChain
- 在一个 Web 应用程序中可以注册多个 Filter 程序,每个 Filter 程序都可以对一个或一组 Servlet 程序进行拦截。如果有多个 Filter 程序都对某个 Servlet 程序的访问过程进行拦截,当针对该 Servlet 的访问请求到达时,Web 容器将把这多个 Filter 程序组合成一个 FilterChain(过滤器链)。
- Filter 链中的各个 Filter 的拦截顺序与它们在 web.xml 文件中的
<filter-mapping>
配置顺序一致,上一个 Filter.doFilter 方法中调用 FilterChain.doFilter 方法将激活下一个 Filter的doFilter 方法,最后一个 Filter.doFilter 方法中调用的 FilterChain.doFilter 方法将激活目标 Servlet 的 service 方法。 - 只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法都不会被执行。
重要知识点
多个过滤器的执行顺序
多个过滤器的执行顺序由web.xml中配置的<filter-mapping>
决定的,在前的先拦截,在后的后拦截,但是<filter-mapping>
一定要声明在<filter>
之后,即要先注册Filter再映射Filter。
<url-pattern>的配置规则
- 精确匹配:需设置一个完整的路径
例如:<url-pattern>/index.jsp</url-pattern>
,则只有访问index.jsp的时候才拦截请求
- 模糊匹配:通过通配符表示一系列路径
- 前缀匹配。例如:
<url-pattern>/pages/*</url-pattern>
,则只有访问pages目录下的资源就会拦截请求 - 后缀匹配。例如:
<url-pattern>*.jsp</url-pattern>
,则拦截所有的jsp页面
- 前缀匹配。例如:
代码示例
- 项目结构
- Filter
1 | package com.lzumetal.javaweb.filter; |
1 | package com.lzumetal.javaweb.filter; |
- Servlet
1 | package com.lzumetal.javaweb.servlet; |
- web.xml
1 |
|
- 启动项目,在浏览器中访问:http://localhost:8080/javaweb_war/helloServlet ,可以看到控制台上的输出为:
1 | FilterTest02 执行前--- |
- 从上面打印的日志还可以得出一个结论:过滤器的排序靠前的,
filterChain.doFilter()
前的代码先执行,filterChain.doFilter()
后的代码后执行。
SpringBoot项目中添加自定义Filter
在SpringBoot项目中如果要使用Filter,则需要
- 实现 javax.servlet.Filter。
- 自定义Filter上添加注解
@WebFilter
(javax.servlet.annotation.WebFilter
);或者通过@Bean
注解配置的方式注入自定义FilterBean。
@WebFilter注解
1 | package com.lzumetal.springboot.filter; |
@Bean注解注入
如果使用@bean注解,则不再需要在Filter上加@WebFilter注解。
1 | package com.lzumetal.springboot.filter.config; |
设置Filter执行顺序
如果要设置自定义Filter的执行顺序,只能使用@Bean注解注入的方式配置Filter,并且设置FilterRegistrationBean的order属性。1
2
3
4
5
6
7
8
9
10
11 4j
public class RoleFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
log.info("获取用户角色...");
filterChain.doFilter(servletRequest, servletResponse);
}
}
1 | package com.lzumetal.springboot.filter.config; |
执行结果:1
2INFO 15800 --- [nio-8080-exec-1] c.lzumetal.springboot.filter.RoleFilter : 获取用户角色...
INFO 15800 --- [nio-8080-exec-1] c.l.springboot.filter.BlackIpFilter : /testFilter|访问ip|0:0:0:0:0:0:0:1