杰瑞科技汇

Servlet过滤器如何实现请求拦截与处理?

什么是 Servlet 过滤器?

Servlet 过滤器是 Java Web 应用中的一种强大组件,它能够拦截客户端的请求服务器的响应,在请求到达目标 Servlet 或 JSP 之前,过滤器可以对请求进行检查、修改或处理;在响应返回给客户端之前,过滤器也可以对响应进行处理。

Servlet过滤器如何实现请求拦截与处理?-图1
(图片来源网络,侵删)

你可以把它想象成一个安检门或者中间人

  • 对请求:所有人都必须经过安检门,安检门可以检查你是否带了危险品(非法请求),或者帮你整理一下衣冠(修改请求头)。
  • 对响应:所有离开大楼的包裹都要经过检查,可以检查是否夹带了不该带出去的东西(敏感信息),或者给包裹贴上一个统一的标签(添加响应头)。

过滤器的主要作用

过滤器主要用于实现横切关注点,即那些与核心业务逻辑无关但又需要在多个地方被处理的功能,常见的应用场景包括:

  1. 认证与授权:检查用户是否登录,是否有权限访问某个资源。
  2. 日志记录:记录所有请求的详细信息,如 IP 地址、访问时间、请求参数等,用于监控和审计。
  3. 数据压缩:对响应数据进行压缩(如 GZIP),以减少网络传输量,加快页面加载速度。
  4. 字符编码转换:统一处理请求和响应的字符编码,解决中文乱码问题(非常经典和实用的场景)。
  5. 输入验证与净化:对用户输入的数据进行验证(如检查格式)和净化(如防止 XSS 攻击)。
  6. 敏感信息过滤:从响应中过滤掉敏感数据(如身份证号、手机号)。
  7. 资源访问控制:限制某些 IP 地址的访问。

过滤器的工作原理

过滤器的工作依赖于一个名为 Filter Chain(过滤器链)的机制。

  1. 当一个请求匹配到某个 URL 模式时,Web 容器(如 Tomcat)会查找所有配置了该模式的过滤器。
  2. 这些过滤器会按照它们在 web.xml 中声明的顺序(或使用 @WebFilter 注解时类加载的顺序)组成一个过滤器链
  3. 请求会沿着这个链依次传递:
    • 请求到达 Filter 1
    • Filter 1 执行其 doFilter() 方法,在这个方法的开头,它会调用 chain.doFilter(request, response),这个调用就像一个“开关”,它会将请求传递给链中的下一个组件(可能是 Filter 2,也可能是最终的 Servlet)。
    • 当 Servlet 处理完请求并生成响应后,响应会沿着过滤器链反向返回。
    • 响应回到 Filter 2 的 doFilter() 方法中,在 chain.doFilter() 调用之后的代码。
    • 然后响应回到 Filter 1 的 doFilter() 方法中,在 chain.doFilter() 调用之后的代码。
    • 响应被发送回客户端。

关键点chain.doFilter() 之前的代码在请求进入目标资源之前执行,chain.doFilter() 之后的代码在响应离开目标资源之后执行。

Servlet过滤器如何实现请求拦截与处理?-图2
(图片来源网络,侵删)

如何创建和配置一个过滤器?

有两种主要的方式来配置过滤器:传统的 web.xml 方式和现代的注解方式。

使用 @WebFilter 注解(推荐)

这是现代 Servlet (3.0+) 推荐的方式,更加简洁。

创建过滤器类

创建一个类,实现 javax.servlet.Filter 接口。

Servlet过滤器如何实现请求拦截与处理?-图3
(图片来源网络,侵删)
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// @WebFilter 注解用于指定此过滤器要拦截哪些 URL
// "/*" 表示拦截所有请求
@WebFilter("/*")
public class LoggingFilter implements Filter {
    // 初始化方法,Web 容器启动时调用,只调用一次
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("LoggingFilter 初始化...");
    }
    // 核心过滤方法,每次请求匹配时都会调用
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 将请求和响应对象转换为 HttpServletRequest 和 HttpServletResponse
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        // --- 请求处理 ---
        System.out.println("请求到达: " + req.getRequestURI() + " 来自: " + req.getRemoteAddr());
        // 将请求传递给下一个过滤器或目标 Servlet
        // 这是过滤器链的核心!
        chain.doFilter(request, response);
        // --- 响应处理 ---
        System.out.println("响应离开: " + req.getRequestURI());
    }
    // 销毁方法,Web 容器关闭时调用,只调用一次
    @Override
    public void destroy() {
        System.out.println("LoggingFilter 销毁...");
    }
}

部署

确保你的项目是 Servlet 3.0+ 版本,并且你的 Web 容器(如 Tomcat 7+)支持,将编译后的 .class 文件放在 WEB-INF/classes 目录下即可,容器会自动扫描并加载带有 @WebFilter 注解的类。

使用 web.xml 部署描述符(传统方式)

创建过滤器类

实现 Filter 接口,但不需要加任何注解。

// 这个类和上面注解方式的类一样,只是没有 @WebFilter 注解
public class LoggingFilter implements Filter {
    // ... init, doFilter, destroy 方法实现同上 ...
}

配置 web.xml

WEB-INF/web.xml 文件中添加以下配置:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 1. 定义过滤器 -->
    <filter>
        <!-- 过滤器的唯一名称 -->
        <filter-name>loggingFilter</filter-name>
        <!-- 过滤器的实现类全限定名 -->
        <filter-class>com.example.LoggingFilter</filter-class>
    </filter>
    <!-- 2. 将过滤器映射到 URL 模式 -->
    <filter-mapping>
        <!-- 引用上面定义的过滤器名称 -->
        <filter-name>loggingFilter</filter-name>
        <!-- 指定要拦截的 URL 模式 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

实战案例:字符编码过滤器

这是一个非常经典且实用的过滤器,用于解决 POST 请求和响应的中文乱码问题。

编码过滤器类 CharacterEncodingFilter.java

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {
    private String encoding = "UTF-8";
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 可以从 web.xml 中读取配置的编码
        String encodingParam = filterConfig.getInitParameter("encoding");
        if (encodingParam != null) {
            this.encoding = encodingParam;
        }
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 设置请求的字符编码
        request.setCharacterEncoding(encoding);
        // 设置响应的字符编码
        response.setCharacterEncoding(encoding);
        // 也可以设置响应头,更通用
        // response.setContentType("text/html;charset=" + encoding);
        // 将请求和响应传递下去
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
        // 清理资源
    }
}

web.xml 中配置(可选)

如果你想通过 web.xml 来配置编码,可以这样写:

<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>com.example.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

过滤器链的顺序

当有多个过滤器时,它们的执行顺序非常重要。

  • 使用 @WebFilter:顺序通常由容器的类加载机制决定,不太可靠,为了精确控制顺序,可以给过滤器类添加 @Order 注解(如果项目使用了 Spring 框架)或者使用 javax.annotation.Priority 注解,但在纯 Servlet 环境中,推荐使用 web.xml
  • 使用 web.xml:顺序是严格按照 <filter-mapping>web.xml 中出现的顺序来执行的,先定义的先执行。
<!-- 这个过滤器会先执行 -->
<filter-mapping>
    <filter-name>filterA</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 这个过滤器会后执行 -->
<filter-mapping>
    <filter-name>filterB</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

请求流程:Client -> Filter A -> Filter B -> Servlet -> Filter B -> Filter A -> Client

特性 描述
核心接口 javax.servlet.Filter
核心方法 doFilter(ServletRequest, ServletResponse, FilterChain)
核心机制 chain.doFilter() 调用,将控制权传递给下一个组件。
配置方式 注解 @WebFilter("url-pattern") (推荐,简洁)
XML web.xml 中的 <filter><filter-mapping> (传统,可控性强)
生命周期 init() -> doFilter() (多次) -> destroy()
主要用途 认证、授权、日志、编码、压缩、数据净化等横切功能。

Servlet 过滤器是 Java Web 开发中不可或缺的工具,它提供了一种优雅、可复用的方式来处理与业务逻辑无关的系统级任务,极大地提高了代码的模块化和可维护性。

分享:
扫描分享到社交APP
上一篇
下一篇