Java Web Filter

Filter,即过滤器,可以将请求拦截下来,完成一些特殊的功能。

一、什么是 Filter ?

Filter 是 Java Web 中的过滤器,可以拦截请求和响应,并对它们做预处理和后处理。

Filter 一般用于完成某些通用的操作,例如:

  • 登录验证
  • 统一编码方式处理
  • 敏感字符过滤
  • ···

二、生命周期方法

1. init()

服务器启动后,Filter 对象被创建,init() 方法被执行。

该方法只执行一次,用于加载资源。

2. doFilter()

1
2
3
4
5
6
7
8
9
10
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
// 预处理
···

// 放行
chain.doFilter(request, response);

// 后处理
···
}
  • 具体执行流程是:

    • 在调用 Servlet 之前先进行拦截
    • 放行,调用 Servlet
    • Servlet 处理结束后,再在发送回客户端之前进行拦截
  • chain.doFilter(request, response):放行

  • 一般在预处理处对 request 进行预处理,

    在后处理处对 response 进行后处理

3. destory()

在服务器正常关闭后,Filter 对象被销毁,destory() 方法被执行。

该方法只执行一次,用于释放资源。

三、步骤

  • 定义类,实现接口 Filter
  • 重写 doFilter() 方法
  • 配置 Filter

四、拦截路径配置

1. XML 配置

打开 web.xml 文件,添加 Filter 配置信息:

1
2
3
4
5
6
7
8
<filter>
<filter-name>名字</filter-name>
<filter-class>全类名</filter-class>
</filter>
<filter-mapping>
<filter-name>名字</filter-name>
<url-pattern>/拦截路径</url-pattern>
</filter-mapping>

2. 注解配置

在 Filter 实现类上使用 @WebFilter 进行注解:

1
@WebFilter("/拦截路径")

3. 拦截路径

  • 具体资源路径:只有访问具体资源时,过滤器才会被执行

    1
    2
    /index.html
    /hello
  • 目录:访问指定目录下的所有资源,过滤器都会被执行

    1
    /user/*
  • 后缀名:访问所有指定后缀名的资源时,过滤器都会执行

    1
    *.jsp
  • 所有资源:访问任何资源,过滤器都会执行

    1
    /*

五、拦截方式配置

1. XML 配置

打开 web.xml 文件,添加 Filter 配置信息,添加 dispatcherTypes 属性:

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>名字</filter-name>
<filter-class>全类名</filter-class>
</filter>
<filter-mapping>
<filter-name>名字</filter-name>
<url-pattern>/拦截路径</url-pattern>
<dispatcher>拦截方式</dispatcher>
</filter-mapping>

2. 注解配置

在 Filter 实现类上使用 @WebFilter 进行注解,为注解添加 dispatcherTypes 属性:

1
2
3
4
@WebFilter(
urlPatterns="/拦截路径",
dispatcherTypes={DispatcherType.拦截方式, [DispatcherType.拦截方式2, ···]}
)

3. 拦截方式

  • REQUEST:默认值,只拦截由客户端直接发起的请求

  • FORWARD:只拦截转发的请求

  • INCLUDE:只拦截 JSP 的 include 动作

    通过 <jsp:include page="xxx.jsp" />,可以将一个 JSP 嵌入到另一个 JSP 中,这种做法被称为 include 动作,每通过 include 动作嵌入的一个页面,都会被 INCLUDE 过滤器过滤

  • ERROR:只拦截错误请求

    当进行了错误的请求,响应状态码为 4XX 或 5XX 时,便会被 ERROR 的过滤器拦截

  • ASYNC:只拦截异步访问资源

六、多个过滤器

1. 执行顺序

(1) 注解配置

按照过滤器类名字典序排序比较,值小的先执行

(2) XML 配置

按照过滤器在 XML 中的配置顺序,配置在上面的先执行

2. 执行流程

假设有过滤器 1 和过滤器 2,且过滤器 1 执行顺序先于过滤器 2,则执行流程为:

  • 过滤器 1 预处理
  • 过滤器 2 预处理
  • Servlet 处理
  • 过滤器 2 后处理
  • 过滤器 1 后处理

七、案例 - 登录验证

需求:

判断请求是否与登录相关,

  • 若相关,放行
  • 若不相关,判断用户是否登录
    • 如果已登录,放行
    • 如果未登录,跳转至登录页

代码:

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
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
// 将 ServletRequest 强制转换为 HttpServletRequest
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 获取资源请求路径
String requestURI = httpServletRequest.getRequestURI();
// 根据路径判断资源是否与登录相关,应该将login依赖的静态资源也一共排除
if (requestURI.contains("login")) {
// 放行
chain.doFilter(request, response);
} else {
// 简单校验,根据Session中是否有user判断是否登录
Object user = httpServletRequest.getSession().getAttribute("user");
if (user != null) {
// 放行
chain.doFilter(request, response);
} else {
// 请求转发至登录页面
request.getRequestDispatcher("/login.html").forward(request, response);
}
}
}

public void init(FilterConfig config) throws ServletException {
}

public void destroy() {
}
}

结果:

未登录时,所有对非 login 的请求将会统一被转发至 login.html:

包含 login 的请求将可以正常访问:

参考