0%

过滤器

Java 过滤器(Filter):Java Web 中的请求与响应拦截器

过滤器(Filter)是 Java Web 中用于拦截和处理请求 / 响应的组件,基于 Servlet 容器的函数回调机制工作。它可以在请求到达目标资源(如 Servlet、JSP)前预处理请求,或在响应返回客户端前处理响应,常用于身份验证、日志记录、数据转换等场景。本文将详细解析 Filter 的工作原理、使用方式及典型应用。

Filter 核心概念与作用

什么是 Filter?

Filter 是实现 javax.servlet.Filter 接口的 Java 类,由 Servlet 容器管理,主要作用包括:

  • 请求拦截:在请求到达目标资源前进行处理(如验证登录状态、过滤非法参数)。
  • 响应处理:在响应返回客户端前进行处理(如压缩数据、添加统一响应头)。
  • 链式处理:多个 Filter 可组成过滤器链,按顺序对请求 / 响应进行多级处理。

常见 Filter 类型

根据功能,Filter 可分为以下类型:

  • 身份验证过滤器:验证用户登录状态,未登录则重定向到登录页。
  • 日志过滤器:记录请求 URL、访问时间、客户端 IP 等信息。
  • 数据压缩过滤器:对响应数据进行 GZIP 压缩,减少传输量。
  • 编码过滤器:统一设置请求 / 响应的字符编码(如 UTF-8)。
  • XSS 过滤器:过滤请求中的恶意脚本,防止跨站脚本攻击。

Filter 接口与生命周期

Filter 接口核心方法

1
2
3
4
5
6
7
8
9
10
11
public interface Filter {
// 初始化:容器启动时调用,用于加载配置(如初始化参数)
void init(FilterConfig config) throws ServletException;

// 拦截处理:每次请求/响应经过过滤器时调用,核心业务逻辑实现
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;

// 销毁:容器关闭时调用,释放资源(如关闭连接、清理缓存)
void destroy();
}

生命周期

Filter 的生命周期由 Servlet 容器管理,分为三个阶段:

  1. 初始化(init
    • 触发时机:Web 应用启动时(或 Filter 首次被调用时,取决于容器配置)。
    • 操作:通过 FilterConfig 获取初始化参数(如 web.xml 中配置的参数),初始化资源。
  2. 拦截处理(doFilter
    • 触发时机:每次请求匹配 Filter 的拦截规则时。
    • 操作:对 ServletRequest/ServletResponse 进行处理,调用 FilterChain.doFilter() 将请求传递给下一个 Filter 或目标资源。
  3. 销毁(destroy
    • 触发时机:Web 应用卸载或容器关闭时。
    • 操作:释放资源(如关闭文件流、数据库连接)。

FilterChainFilterConfig

  • FilterChain:表示过滤器链,调用其 doFilter(request, response) 方法可将请求传递给下一个 Filter 或目标资源。若不调用此方法,请求会被拦截,无法到达目标资源。

  • FilterConfig:用于获取 Filter 的配置信息,如初始化参数、Servlet 上下文等:

    1
    2
    3
    4
    5
    6
    // 获取初始化参数
    String username = filterConfig.getInitParameter("userName");
    // 获取所有参数名称
    Enumeration<String> paramNames = filterConfig.getInitParameterNames();
    // 获取 ServletContext
    ServletContext context = filterConfig.getServletContext();

Filter 的配置与使用

Filter 需通过配置指定拦截规则(如拦截的 URL 路径、Servlet 名称),配置方式有两种:XML 配置注解配置(Servlet 3.0+)。

XML 配置(web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 注册 Filter -->
<filter>
<filter-name>SecurityFilter</filter-name> <!-- 过滤器名称(唯一) -->
<filter-class>com.example.SecurityFilter</filter-class> <!-- 全类名 -->
<!-- 初始化参数(可选) -->
<init-param>
<param-name>allowIP</param-name>
<param-value>192.168.1.100</param-value>
</init-param>
</filter>

<!-- 配置拦截规则 -->
<filter-mapping>
<filter-name>SecurityFilter</filter-name> <!-- 与注册名称一致 -->
<url-pattern>/*</url-pattern> <!-- 拦截所有请求 -->
<!-- 可选:指定拦截的请求方式(默认仅拦截直接请求) -->
<dispatcher>REQUEST</dispatcher> <!-- 直接访问资源 -->
<dispatcher>FORWARD</dispatcher> <!-- 转发访问资源 -->
</filter-mapping>
  • url-pattern:指定拦截的 URL 路径,支持通配符(如 /* 拦截所有请求,/admin/* 拦截 admin 路径下的请求)。
  • servlet-name:可替代 url-pattern,指定拦截特定 Servlet(如 <servlet-name>UserServlet</servlet-name>)。
  • dispatcher:指定拦截的请求类型,可选值:
    • REQUEST:直接访问资源(默认);
    • FORWARD:通过 RequestDispatcher.forward() 转发的请求;
    • INCLUDE:通过 RequestDispatcher.include() 包含的请求;
    • ERROR:通过声明式异常处理(如 web.xml<error-page>)跳转的请求。

注解配置(Servlet 3.0+)

无需 web.xml,直接在 Filter 类上添加 @WebFilter 注解:

1
2
3
4
5
6
7
8
9
@WebFilter(
filterName = "SecurityFilter",
urlPatterns = "/*", // 拦截所有请求
initParams = {@WebInitParam(name = "allowIP", value = "192.168.1.100")},
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD}
)
public class SecurityFilter implements Filter {
// 实现接口方法...
}

示例:登录验证 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
25
26
27
28
29
30
31
32
33
34
public class LoginFilter implements Filter {
private FilterConfig filterConfig;

@Override
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;

// 获取当前会话中的用户
HttpSession session = request.getSession();
String user = (String) session.getAttribute("user");

// 若未登录且请求的是受保护资源,则重定向到登录页
String requestURI = request.getRequestURI();
if (user == null && requestURI.contains("/admin/")) {
response.sendRedirect(request.getContextPath() + "/login.jsp");
return; // 终止流程,不传递给下一个组件
}

// 若已登录或访问的是公开资源,继续传递请求
chain.doFilter(request, response);
}

@Override
public void destroy() {
// 释放资源(如关闭连接)
}
}

过滤器链(Filter Chain)

当多个 Filter 匹配同一请求时,容器会按 filter-mapping 在配置中的顺序 组成过滤器链,依次调用各 Filter 的 doFilter 方法。

执行流程

  1. 客户端发送请求 → 容器根据 URL 匹配 Filter 链。
  2. 按顺序调用第一个 Filter 的 doFilter 方法。
  3. 每个 Filter 处理后,调用 chain.doFilter() 传递给下一个 Filter。
  4. 最后一个 Filter 调用 chain.doFilter() 后,请求到达目标资源(如 Servlet)。
  5. 目标资源处理完成后,响应按 Filter 链的逆序返回,每个 Filter 可继续处理响应。

示例:两个 Filter 的执行顺序

1
2
3
4
5
6
7
8
9
<!-- 过滤器链顺序:FirstFilter → SecondFilter -->
<filter-mapping>
<filter-name>FirstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SecondFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

执行流程:

1
2
请求 → FirstFilter.doFilter() → chain.doFilter() → SecondFilter.doFilter() → chain.doFilter() → 目标资源
响应 → SecondFilter(继续处理) → FirstFilter(继续处理) → 客户端

Filter 与 Servlet 的区别

特性 Filter Servlet
核心功能 拦截请求 / 响应,预处理 / 后处理 处理请求,生成响应
触发时机 请求到达 Servlet 前 / 响应返回前 请求匹配 URL 映射时
生命周期方法 init()doFilter()destroy() init()service()destroy()
调用方式 链式调用(FilterChain 直接调用(容器匹配 URL 后)
典型应用 身份验证、日志、编码处理 业务逻辑处理、数据交互

最佳实践

  1. 明确拦截范围:避免使用 /* 拦截所有请求,应根据功能精准配置 url-pattern(如 /api/* 仅拦截 API 请求)。
  2. 控制过滤器链顺序:按依赖关系排序(如先执行编码 Filter,再执行身份验证 Filter)。
  3. 及时释放资源:在 destroy() 方法中清理资源(如关闭文件流、线程池),避免内存泄漏。
  4. 避免长时间操作doFilter 方法应快速执行,耗时操作(如复杂计算)会阻塞请求处理

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10