一、过滤器基础概念
1. 什么是过滤器
过滤器(Filter)是Java Web中的一种组件,可以在请求到达Servlet之前或响应返回客户端之前对HTTP请求和响应进行预处理和后处理。
2. 过滤器的作用
-
认证和授权:检查用户是否登录,是否有权限访问资源
-
日志记录:记录请求和响应信息
-
数据压缩:压缩响应数据
-
字符编码转换:统一请求和响应的编码
-
数据加密/解密:对敏感数据进行处理
-
XSS防护:过滤恶意脚本
3. 过滤器的工作原理
客户端请求 → 过滤器1 → 过滤器2 → ... → Servlet → 过滤器2 → 过滤器1 → 客户端响应
二、过滤器的创建与配置
1. 创建过滤器类
实现javax.servlet.Filter
接口:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*") // 过滤所有请求
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化方法,在过滤器创建时调用
System.out.println("过滤器初始化...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 过滤处理方法
System.out.println("请求到达过滤器...");
// 预处理代码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 放行到下一个过滤器或目标资源
chain.doFilter(request, response);
// 后处理代码
System.out.println("响应经过过滤器...");
}
@Override
public void destroy() {
// 销毁方法,在过滤器销毁时调用
System.out.println("过滤器销毁...");
}
}
2. 配置过滤器的两种方式
方式一:注解配置(Servlet 3.0+)
@WebFilter(
urlPatterns = {"/user/*", "/admin/*"}, // 过滤路径
initParams = {
@WebInitParam(name = "encoding", value = "UTF-8"),
@WebInitParam(name = "key", value = "value")
},
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD}
)
public class MyFilter implements Filter {
// ...
}
方式二:web.xml配置
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
三、过滤器的核心方法详解
1. init(FilterConfig config)
-
在过滤器实例创建后立即调用
-
用于初始化过滤器的配置参数
-
FilterConfig
对象可以获取初始化参数和ServletContext@Override public void init(FilterConfig filterConfig) { String encoding = filterConfig.getInitParameter("encoding"); this.encoding = encoding != null ? encoding : "UTF-8"; this.servletContext = filterConfig.getServletContext(); }
2. doFilter(ServletRequest, ServletResponse, FilterChain)
-
每次请求都会调用该方法
-
核心过滤逻辑写在这里
-
必须调用
chain.doFilter()
方法将请求传递给下一个过滤器或目标资源@Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; // 预处理 System.out.println("拦截到请求: " + request.getRequestURI()); long startTime = System.currentTimeMillis(); // 放行 chain.doFilter(request, response); // 后处理 long endTime = System.currentTimeMillis(); System.out.println("请求处理耗时: " + (endTime - startTime) + "ms"); }
3. destroy()
-
在过滤器实例销毁前调用
-
用于释放资源
@Override public void destroy() { // 清理资源 this.encoding = null; this.servletContext = null; }
四、过滤器的高级应用
1. 多过滤器执行顺序
多个过滤器的执行顺序:
-
web.xml中
<filter-mapping>
的配置顺序 -
注解配置时按过滤器类名的字母顺序
可以通过@Order
注解指定顺序(Spring环境中):
@Order(1)
@WebFilter("/*")
public class FilterA implements Filter { /*...*/ }
@Order(2)
@WebFilter("/*")
public class FilterB implements Filter { /*...*/ }
2. 拦截不同类型的请求
通过dispatcherTypes
属性可以指定拦截的请求类型:
@WebFilter(
urlPatterns = "/*",
dispatcherTypes = {
DispatcherType.REQUEST, // 直接请求
DispatcherType.FORWARD, // 转发
DispatcherType.INCLUDE, // 包含
DispatcherType.ERROR, // 错误
DispatcherType.ASYNC // 异步
}
)
3. 防止XSS攻击的过滤器示例
@WebFilter("/*")
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(request);
chain.doFilter(xssRequest, resp);
}
// XSS请求包装类
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
return cleanXSS(super.getParameter(name));
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) return null;
String[] cleanedValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
cleanedValues[i] = cleanXSS(values[i]);
}
return cleanedValues;
}
private String cleanXSS(String value) {
if (value == null) return null;
return value.replaceAll("<", "<").replaceAll(">", ">");
}
}
}
4. 登录验证过滤器示例
@WebFilter("/admin/*")
public class LoginFilter implements Filter {
@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(false);
String loginURI = request.getContextPath() + "/login";
boolean loggedIn = session != null && session.getAttribute("user") != null;
boolean loginRequest = request.getRequestURI().equals(loginURI);
if (loggedIn || loginRequest) {
chain.doFilter(request, response);
} else {
response.sendRedirect(loginURI);
}
}
}
五、常见问题与解决方案
1. 过滤器不生效的可能原因
-
过滤器类没有实现
javax.servlet.Filter
接口 -
没有配置
@WebFilter
注解或web.xml配置错误 -
URL模式不匹配当前请求路径
-
过滤器抛出了异常没有正确处理
-
在
doFilter
方法中没有调用chain.doFilter()
2. 获取不到HttpServletRequest对象
需要将ServletRequest强制转换为HttpServletRequest:
HttpServletRequest httpRequest = (HttpServletRequest) request;
3. 过滤器中的异常处理
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
try {
// 预处理
chain.doFilter(req, resp);
// 后处理
} catch (Exception e) {
// 处理异常
((HttpServletResponse) resp).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
4. 性能监控过滤器示例
@WebFilter("/*")
public class PerformanceFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
chain.doFilter(req, resp);
long endTime = System.currentTimeMillis();
HttpServletRequest request = (HttpServletRequest) req;
System.out.printf("%s 请求 %s 耗时 %d ms%n",
request.getMethod(),
request.getRequestURI(),
(endTime - startTime));
}
}
六、Spring Boot中的过滤器
在Spring Boot中使用过滤器:
1. 使用@WebFilter注解
@WebFilter(urlPatterns = "/*")
public class MySpringFilter implements Filter {
// 实现方法
}
需要在启动类添加@ServletComponentScan
注解:
@SpringBootApplication
@ServletComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2. 使用FilterRegistrationBean注册
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new MyFilter());
registration.addUrlPatterns("/*");
registration.setName("myFilter");
registration.setOrder(1); // 设置顺序
return registration;
}
}
3. Spring Security中的过滤器链
Spring Security本身就是基于过滤器链实现的,了解其过滤器顺序有助于自定义过滤器:
-
ChannelProcessingFilter
-
SecurityContextPersistenceFilter
-
ConcurrentSessionFilter
-
认证处理过滤器(UsernamePasswordAuthenticationFilter等)
-
SecurityContextHolderAwareRequestFilter
-
RememberMeAuthenticationFilter
-
AnonymousAuthenticationFilter
-
ExceptionTranslationFilter
-
FilterSecurityInterceptor
七、最佳实践
-
合理设计过滤器链:将功能单一化,每个过滤器只处理一件事情
-
注意性能影响:过滤器中避免耗时操作
-
异常处理:确保过滤器中的异常被正确处理
-
线程安全:过滤器是单例的,注意实例变量的线程安全
-
合理设置过滤路径:避免过度拦截影响性能
-
日志记录:重要的过滤操作应该记录日志
-
与拦截器区分:在Spring环境中,简单功能用拦截器,Servlet相关用过滤器
八、总结
过滤器是Java Web开发中非常重要的组件,它提供了一种声明式的处理HTTP请求和响应的机制。通过本文的详细介绍,你应该已经掌握了:
-
过滤器的基本概念和工作原理
-
如何创建和配置过滤器
-
过滤器的核心方法和生命周期
-
过滤器的高级应用场景
-
常见问题解决方案
-
Spring Boot中的过滤器使用
-
过滤器的最佳实践
合理使用过滤器可以大大提高Web应用的安全性、可维护性和性能。