介绍
概念
客户端发送请求(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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.lzumetal.javaweb.filter;
import javax.servlet.*; import java.io.IOException;
public class FilterTest01 implements Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { System.out.println("FilterTest01 执行前---"); filterChain.doFilter(request, response); System.out.println("FilterTest01 执行后---"); }
@Override public void destroy() {
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.lzumetal.javaweb.filter;
import javax.servlet.*; import java.io.IOException;
public class FilterTest02 implements Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { System.out.println("FilterTest02 执行前---"); filterChain.doFilter(request, response); System.out.println("FilterTest02 执行后---"); }
@Override public void destroy() {
} }
|
- Servlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.lzumetal.javaweb.servlet;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebServlet("/helloServlet") public class HelloServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("执行HelloServlet的目标方法"); } }
|
- web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app> <display-name>Archetype Created Web Application</display-name>
<filter> <filter-name>FilterTest01</filter-name> <filter-class>com.lzumetal.javaweb.filter.FilterTest01</filter-class> </filter> <filter> <filter-name>FilterTest02</filter-name> <filter-class>com.lzumetal.javaweb.filter.FilterTest02</filter-class> </filter>
<filter-mapping> <filter-name>FilterTest02</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<filter-mapping> <filter-name>FilterTest01</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
</web-app>
|
- 启动项目,在浏览器中访问:http://localhost:8080/javaweb_war/helloServlet ,可以看到控制台上的输出为:
1 2 3 4 5
| FilterTest02 执行前--- FilterTest01 执行前--- 执行HelloServlet的目标方法 FilterTest01 执行后--- FilterTest02 执行后---
|
- 从上面打印的日志还可以得出一个结论:过滤器的排序靠前的,
filterChain.doFilter()前的代码先执行,filterChain.doFilter()后的代码后执行。
SpringBoot项目中添加自定义Filter
在SpringBoot项目中如果要使用Filter,则需要
- 实现 javax.servlet.Filter。
- 自定义Filter上添加注解
@WebFilter(javax.servlet.annotation.WebFilter);或者通过@Bean注解配置的方式注入自定义FilterBean。
@WebFilter注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.lzumetal.springboot.filter;
import com.lzumetal.springboot.utils.IPUtil; import lombok.extern.slf4j.Slf4j;
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException;
@Slf4j @WebFilter(urlPatterns = "/*", filterName = "blackIpFilter") public class BlackIpFilter implements Filter {
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; String ipAddr = IPUtil.getIpAddr(request); String requestURI = request.getRequestURI(); log.info("{}|访问ip|{}", requestURI, ipAddr); filterChain.doFilter(servletRequest, servletResponse); }
}
|
@Bean注解注入
如果使用@bean注解,则不再需要在Filter上加@WebFilter注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.lzumetal.springboot.filter.config;
import com.lzumetal.springboot.filter.BlackIpFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class FilterConfiguration {
@Bean public FilterRegistrationBean blackIpFilter() { FilterRegistrationBean<BlackIpFilter> filterRegistrationBean = new FilterRegistrationBean<>(); BlackIpFilter blackIpFilter = new BlackIpFilter(); filterRegistrationBean.setFilter(blackIpFilter); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("name","hahaha"); filterRegistrationBean.setName("blackIpFilter"); filterRegistrationBean.setOrder(1); return filterRegistrationBean; }
}
|
设置Filter执行顺序
如果要设置自定义Filter的执行顺序,只能使用@Bean注解注入的方式配置Filter,并且设置FilterRegistrationBean的order属性。
1 2 3 4 5 6 7 8 9 10 11
| @Slf4j public class RoleFilter implements Filter {
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; log.info("获取用户角色..."); filterChain.doFilter(servletRequest, servletResponse); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.lzumetal.springboot.filter.config;
import com.lzumetal.springboot.filter.BlackIpFilter; import com.lzumetal.springboot.filter.RoleFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class FilterConfiguration {
@Bean public FilterRegistrationBean blackIpFilter() { FilterRegistrationBean<BlackIpFilter> filterRegistrationBean = new FilterRegistrationBean<>(); BlackIpFilter blackIpFilter = new BlackIpFilter(); filterRegistrationBean.setFilter(blackIpFilter); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("name","hahaha"); filterRegistrationBean.setName("blackIpFilter"); filterRegistrationBean.setOrder(2); return filterRegistrationBean; }
@Bean public FilterRegistrationBean roleFilter() { FilterRegistrationBean<RoleFilter> filterRegistrationBean = new FilterRegistrationBean<>(); RoleFilter roleFilter = new RoleFilter(); filterRegistrationBean.setFilter(roleFilter); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("key","value001"); filterRegistrationBean.setName("roleFilter"); filterRegistrationBean.setOrder(1); return filterRegistrationBean; }
}
|
执行结果:
1 2
| INFO 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
|