过滤器(Filter)的拦截与放行原理:Web应用的“守门人”机制

在JavaWeb开发中,过滤器(Filter)就像Web应用的“守门人”,能在请求到达目标资源(Servlet、JSP等)前拦截并处理,也能在响应返回客户端前进行加工。从统一编码处理到权限验证,Filter的拦截与放行机制是实现这些功能的核心。本文将深入解析Filter的工作原理,拆解拦截与放行的底层逻辑。

一、Filter的核心定位:请求与响应的“中间处理器”

Filter是JavaEE规范定义的组件,运行在Web服务器(如Tomcat)中,位于客户端与目标资源之间,形成“客户端→Filter→目标资源→Filter→客户端”的处理链路。其核心作用是:

  • 拦截请求:在请求到达Servlet/JSP前,对请求进行检查(如登录状态、参数合法性)、修改(如统一编码)或阻断(如拒绝未登录用户)。
  • 拦截响应:在响应返回客户端前,对响应数据进行处理(如压缩、添加版权信息)。
  • 链式处理:多个Filter可按顺序组成“过滤器链”,共同处理请求与响应(如先验证登录,再检查权限,最后设置编码)。

二、Filter的拦截原理:从注册到执行的完整链路

Filter的拦截能力源于Web服务器对请求的“预处理”机制,其工作流程可拆解为注册映射→请求拦截→链上传递→响应拦截四个阶段。

1. 注册与映射:告诉服务器“要拦截什么”

Filter需要通过配置(XML或注解)告诉服务器:“我要拦截哪些资源”。只有被映射的请求,才会触发Filter的处理逻辑。

(1)注解配置(Servlet 3.0+)

通过@WebFilter注解指定拦截的URL模式(如/*拦截所有请求,/admin/*拦截管理员路径):

// 拦截所有以/admin/开头的请求
@WebFilter(urlPatterns = "/admin/*")
public class AdminFilter implements Filter {
// 实现Filter接口方法...
}
(2)XML配置(web.xml)

web.xml中通过<filter><filter-mapping>标签配置:

<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.example.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
  <!-- 拦截所有请求 -->
  <url-pattern>/*</url-pattern>
  </filter-mapping>

关键urlPatterns决定了Filter的拦截范围,服务器接收请求后,会先匹配所有Filter的映射规则,符合条件的Filter将按顺序加入处理链。

2. 请求拦截:doFilter方法的“关卡作用”

当请求匹配Filter的映射规则时,服务器会调用Filter的doFilter(ServletRequest, ServletResponse, FilterChain)方法,这是拦截逻辑的核心实现处。

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 1. 请求到达目标资源前的拦截逻辑(如登录验证、编码设置)
System.out.println("Filter拦截到请求...");
// 2. 放行:让请求继续流向目标资源或下一个Filter
chain.doFilter(request, response);
// 3. 响应返回客户端前的拦截逻辑(如添加响应头)
System.out.println("Filter拦截到响应...");
}

拦截的本质:服务器在将请求转发给目标资源前,会先执行doFilter方法中chain.doFilter()之前的代码。开发者可在此处对请求进行检查(如判断用户是否登录),若不满足条件,可直接返回响应(如重定向到登录页),从而阻断请求到达目标资源。

3. 放行机制:FilterChain的“接力传递”

FilterChain(过滤器链)是实现放行的核心对象,其doFilter()方法的作用是:让请求继续向后传递——可能是下一个Filter,也可能是最终的目标资源(Servlet/JSP)。

(1)单Filter场景

当只有一个Filter时,chain.doFilter()直接将请求传递给目标资源:

客户端请求 → Filter的doFilter() → chain.doFilter() → 目标资源(Servlet)
                                          ↓
客户端响应 ← Filter的doFilter() ← 目标资源处理完成
(2)多Filter场景(过滤器链)

多个Filter按注册顺序组成链条,每个Filter的chain.doFilter()将请求传递给下一个Filter,直到最后一个Filter的chain.doFilter()触发目标资源处理:

客户端请求 → Filter1.doFilter() → chain.doFilter() → Filter2.doFilter() → chain.doFilter() → 目标资源
                                                                 ↓
客户端响应 ← Filter1.doFilter() ← chain.doFilter()返回 ← Filter2.doFilter() ← 目标资源处理完成

注意:Filter的执行顺序由配置决定——XML中按<filter-mapping>的先后顺序,注解配置则按类名的字母顺序(建议复杂场景用XML显式控制顺序)。

4. 响应拦截:chain.doFilter()之后的“后置处理”

chain.doFilter()方法会阻塞等待目标资源或下一个Filter处理完成,当目标资源生成响应后,程序会回到当前Filter,执行chain.doFilter()之后的代码——这就是响应拦截的实现原理。

例如,在响应返回前添加统一的响应头:

@Override
public void doFilter(...) {
// 请求拦截逻辑...
chain.doFilter(request, response); // 等待目标资源处理
// 响应拦截逻辑:添加版权信息头
response.setHeader("X-Powered-By", "MyWebApp/1.0");
}

三、实战:用Filter实现登录验证(拦截与放行案例)

以“未登录用户禁止访问后台页面”为例,展示Filter如何通过拦截与放行控制请求:

1. 过滤器实现

@WebFilter(urlPatterns = "/admin/*") // 拦截所有后台请求
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
// 转换为HttpServletRequest(获取Session需要)
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 1. 拦截请求:检查Session中是否有登录用户
Object loginUser = request.getSession().getAttribute("loginUser");
if (loginUser == null) {
// 未登录:阻断请求,重定向到登录页(不放行)
response.sendRedirect(request.getContextPath() + "/login.jsp");
return; // 终止后续代码执行
} else {
// 已登录:放行,让请求继续访问后台资源
chain.doFilter(request, response);
}
}
// 初始化与销毁方法(默认实现即可)
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}

2. 拦截与放行逻辑解析

  • 拦截未登录请求:当未登录用户访问/admin/*路径时,Filter检查到loginUsernull,直接通过response.sendRedirect()返回登录页,不调用chain.doFilter(),请求被阻断,无法到达后台资源。
  • 放行已登录请求:已登录用户的请求会触发chain.doFilter(),请求继续传递到/admin/*对应的Servlet或JSP,实现正常访问。