本文目录导读:

我来详细介绍Java Servlet过滤器的开发案例,从基础概念到完整实现。
Servlet过滤器基础
什么是Servlet过滤器
过滤器是Java Web组件,可以在请求到达Servlet之前或响应返回客户端之前进行预处理和后处理。
过滤器的典型应用场景
- 用户认证和授权
- 请求日志记录
- 数据压缩
- 输入验证
- 字符编码设置
- 敏感信息过滤
完整开发案例
案例1:字符编码过滤器
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 字符编码过滤器 - 统一设置请求和响应的字符编码
*/
@WebFilter(urlPatterns = "/*") // 过滤所有请求
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) {
encoding = encodingParam;
}
System.out.println("字符编码过滤器初始化完成,编码:" + encoding);
}
@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);
System.out.println("请求已设置编码为:" + encoding);
// 继续执行后续过滤器或目标Servlet
chain.doFilter(request, response);
System.out.println("响应已处理完成");
}
@Override
public void destroy() {
System.out.println("字符编码过滤器被销毁");
}
}
案例2:用户登录认证过滤器
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 用户登录认证过滤器 - 检查用户是否已登录
*/
@WebFilter(urlPatterns = {"/admin/*", "/user/*"}) // 只过滤管理后台和用户相关页面
public class LoginFilter 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 {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取当前请求的URI
String requestURI = httpRequest.getRequestURI();
String contextPath = httpRequest.getContextPath();
String requestPath = requestURI.substring(contextPath.length());
// 允许访问的公共路径(不需要登录)
if (requestPath.equals("/login") || requestPath.equals("/register")
|| requestPath.equals("/index")) {
chain.doFilter(request, response);
return;
}
// 检查用户是否已登录
HttpSession session = httpRequest.getSession(false);
boolean isLoggedIn = (session != null && session.getAttribute("user") != null);
if (isLoggedIn) {
// 用户已登录,继续执行
System.out.println("用户已认证,允许访问:" + requestPath);
chain.doFilter(request, response);
} else {
// 用户未登录,重定向到登录页面
System.out.println("用户未登录,重定向到登录页面");
httpResponse.sendRedirect(contextPath + "/login");
}
}
@Override
public void destroy() {
System.out.println("登录认证过滤器被销毁");
}
}
案例3:请求日志记录过滤器
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 请求日志记录过滤器 - 记录所有请求信息
*/
@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("日志过滤器初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 收集请求信息
String ip = httpRequest.getRemoteAddr();
String method = httpRequest.getMethod();
String uri = httpRequest.getRequestURI();
String queryString = httpRequest.getQueryString();
String userAgent = httpRequest.getHeader("User-Agent");
String sessionId = httpRequest.getSession().getId();
String timestamp = dateFormat.format(new Date());
// 记录请求开始时间
long startTime = System.currentTimeMillis();
// 构建日志信息
StringBuilder logInfo = new StringBuilder();
logInfo.append("[").append(timestamp).append("] ");
logInfo.append("IP: ").append(ip).append(" ");
logInfo.append("Session: ").append(sessionId).append(" ");
logInfo.append("Method: ").append(method).append(" ");
logInfo.append("URI: ").append(uri);
if (queryString != null) {
logInfo.append("?").append(queryString);
}
logInfo.append(" ").append(userAgent);
// 打印日志
System.out.println("请求开始: " + logInfo.toString());
// 使用包装类记录响应状态和耗时
ResponseWrapper responseWrapper = new ResponseWrapper(httpRequest, response);
try {
// 继续执行
chain.doFilter(request, responseWrapper);
} finally {
// 计算耗时
long duration = System.currentTimeMillis() - startTime;
System.out.println("请求完成: " + uri + " | 耗时: " + duration + "ms");
}
}
@Override
public void destroy() {
System.out.println("日志过滤器被销毁");
}
}
/**
* 响应包装类(如果需要记录响应相关信息)
*/
class ResponseWrapper extends HttpServletRequestWrapper {
private ServletResponse response;
public ResponseWrapper(HttpServletRequest request, ServletResponse response) {
super(request);
this.response = response;
}
}
案例4:XSS(跨站脚本攻击)过滤过滤器
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* XSS过滤器 - 防止跨站脚本攻击
*/
@WebFilter(urlPatterns = "/*")
public class XSSFilter implements Filter {
// XSS攻击模式
private static final Pattern[] XSS_PATTERNS = {
Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("src[\r\n]*=[\r\n]*\\'(.*?)\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE),
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE)
};
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("XSS过滤器初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 使用自定义的请求包装类来过滤参数
chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
}
@Override
public void destroy() {
System.out.println("XSS过滤器被销毁");
}
/**
* XSS请求包装类 - 对请求参数进行XSS过滤
*/
static class XSSRequestWrapper extends HttpServletRequestWrapper {
public XSSRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
@Override
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = stripXSS(values[i]);
}
return encodedValues;
}
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
return stripXSS(value);
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
return stripXSS(value);
}
/**
* 过滤XSS攻击代码
*/
private String stripXSS(String value) {
if (value == null) {
return null;
}
// 对特殊字符进行HTML转义
value = value.replaceAll("&", "&");
value = value.replaceAll("<", "<");
value = value.replaceAll(">", ">");
value = value.replaceAll("\"", """);
value = value.replaceAll("'", "'");
value = value.replaceAll("/", "/");
// 移除XSS攻击模式
for (Pattern pattern : XSS_PATTERNS) {
value = pattern.matcher(value).replaceAll("");
}
return value;
}
}
}
配置方式
使用@WebFilter注解(推荐)
@WebFilter(
urlPatterns = {"/api/*", "/admin/*"},
filterName = "myFilter",
initParams = {
@WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2")
}
)
使用web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 配置字符编码过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>com.example.filter.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>
<!-- 配置登录验证过滤器 -->
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.example.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
<url-pattern>/user/*</url-pattern>
</filter-mapping>
<!-- 配置日志过滤器 -->
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.example.filter.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
过滤器链执行顺序
// 过滤器执行顺序取决于配置顺序(web.xml)或类名(@WebFilter)
@WebFilter(urlPatterns = "/*", filterName = "Filter1")
@Order(1) // 指定执行顺序
public class Filter1 implements Filter { ... }
@WebFilter(urlPatterns = "/*", filterName = "Filter2")
@Order(2)
public class Filter2 implements Filter { ... }
// 执行顺序:Filter1 -> Filter2 -> Servlet -> Filter2 -> Filter1
Maven依赖配置
<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- 如果需要使用注解方式 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
最佳实践建议
- 性能考虑:避免在过滤器中进行耗时操作
- 线程安全:过滤器是单例的,注意线程安全性
- 异常处理:适当处理异常,避免影响整个请求链
- 资源清理:在destroy()方法中释放资源
- 配置灵活:使用初始化参数提高配置灵活性
- 日志记录:适当记录日志便于调试
常见问题排查
- 过滤器未执行:检查URL模式配置是否正确
- 编码问题:确保编码过滤器在其他过滤器之前执行
- 死循环:避免过滤器自身再次调用自身
- 性能问题:尽量减少请求/响应的包装操作
这些案例涵盖了主要的过滤器开发场景,可以根据实际需求进行修改和扩展。