过滤器(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检查到loginUser为null,直接通过response.sendRedirect()返回登录页,不调用chain.doFilter(),请求被阻断,无法到达后台资源。 - 放行已登录请求:已登录用户的请求会触发
chain.doFilter(),请求继续传递到/admin/*对应的Servlet或JSP,实现正常访问。
浙公网安备 33010602011771号