Java Servlet 过滤器链原理 请求包装器与响应修改实战

Java Servlet过滤器链原理与请求/响应包装器实战指南


1. Servlet过滤器概述


Servlet过滤器是Java Web开发中的核心组件,它能够在请求到达Servlet之前和响应返回客户端之前对数据进行预处理和后处理。过滤器通过链式调用的方式组成过滤器链,每个过滤器负责特定的功能,如身份验证、日志记录、数据压缩等。


1.1 过滤器的工作原理


```java
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化代码
}


@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 预处理逻辑
System.out.println("请求到达时间: " + new Date());

// 调用过滤器链中的下一个过滤器
chain.doFilter(request, response);

// 后处理逻辑
System.out.println("响应返回时间: " + new Date());
}

@Override
public void destroy() {
// 清理资源
}

}
```


2. 过滤器链执行机制


2.1 链式调用原理


过滤器链采用责任链模式,按照在web.xml中配置的顺序依次执行:


请求 → Filter1 → Filter2 → ... → FilterN → Servlet → FilterN后处理 → ... → Filter1后处理 → 响应


2.2 配置示例


```xml

LoggingFilter
com.example.LoggingFilter


LoggingFilter
/



AuthFilter
com.example.AuthFilter


AuthFilter
/secure/

```


3. 请求包装器实战


3.1 自定义请求包装器


请求包装器允许我们修改或增强请求参数,常用于参数验证、数据脱敏等场景。


```java
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private final Map modifiedParameters;


public CustomRequestWrapper(HttpServletRequest request) {
super(request);
this.modifiedParameters = new HashMap<>(request.getParameterMap());
}

// 重写获取参数的方法,实现参数过滤
@Override
public String getParameter(String name) {
String[] values = modifiedParameters.get(name);
if (values != null && values.length > 0) {
return sanitize(values[0]); // 参数净化
}
return null;
}

// 添加新参数
public void setParameter(String name, String value) {
modifiedParameters.put(name, new String[]{value});
}

// 参数净化处理
private String sanitize(String input) {
return input.replaceAll("<script>", "").trim();
}

@Override
public Map<String, String[]> getParameterMap() {
return Collections.unmodifiableMap(modifiedParameters);
}

}
```


3.2 在过滤器中使用请求包装器


```java
public class XSSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
CustomRequestWrapper wrappedRequest = new CustomRequestWrapper(httpRequest);


    // 对特定参数进行预处理
if (wrappedRequest.getParameter("content") != null) {
wrappedRequest.setParameter("filteredContent",
"已过滤: " + wrappedRequest.getParameter("content"));
}

chain.doFilter(wrappedRequest, response);
}

}
```


4. 响应包装器实战


4.1 自定义响应包装器


响应包装器用于修改响应内容,常用于数据压缩、内容替换、响应日志等场景。


```java
public class CompressionResponseWrapper extends HttpServletResponseWrapper {
private GZIPServletOutputStream gzipStream;
private PrintWriter printWriter;
private ByteArrayOutputStream buffer;


public CompressionResponseWrapper(HttpServletResponse response) {
super(response);
this.buffer = new ByteArrayOutputStream();
}

@Override
public ServletOutputStream getOutputStream() throws IOException {
if (gzipStream == null) {
gzipStream = new GZIPServletOutputStream(buffer);
}
return gzipStream;
}

@Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
printWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(),
getCharacterEncoding()));
}
return printWriter;
}

// 获取压缩后的数据
public byte[] getCompressedData() throws IOException {
if (gzipStream != null) {
gzipStream.close();
}
if (printWriter != null) {
printWriter.close();
}
return buffer.toByteArray();
}

}


// 自定义GZIP输出流
class GZIPServletOutputStream extends ServletOutputStream {
private final GZIPOutputStream gzipStream;


public GZIPServletOutputStream(OutputStream output) throws IOException {
this.gzipStream = new GZIPOutputStream(output);
}

@Override
public void write(int b) throws IOException {
gzipStream.write(b);
}

@Override
public void close() throws IOException {
gzipStream.close();
}

@Override
public boolean isReady() {
return true;
}

@Override
public void setWriteListener(WriteListener listener) {
// 实现省略
}

}
```


4.2 响应内容修改过滤器


```java
public class ContentModificationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {


    HttpServletResponse httpResponse = (HttpServletResponse) response;
CompressionResponseWrapper wrappedResponse =
new CompressionResponseWrapper(httpResponse);

// 设置响应头,支持gzip压缩
httpResponse.setHeader("Content-Encoding", "gzip");

chain.doFilter(request, wrappedResponse);

// 获取压缩后的数据并写入响应
byte[] compressedData = wrappedResponse.getCompressedData();
response.getOutputStream().write(compressedData);
}

}
```


5. 综合实战案例:API日志记录过滤器


下面是一个完整的实战示例,展示如何记录API请求和响应的详细信息。


```java
public class ApiLoggingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(ApiLoggingFilter.class);


@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

long startTime = System.currentTimeMillis();
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;

// 创建自定义请求包装器记录请求体
BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest);

// 创建自定义响应包装器记录响应体
BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(httpResponse);

try {
// 记录请求信息
logRequestInfo(bufferedRequest);

// 继续过滤器链
chain.doFilter(bufferedRequest, bufferedResponse);

} finally {
// 记录响应信息
long duration = System.currentTimeMillis() - startTime;
logResponseInfo(bufferedRequest, bufferedResponse, duration);

// 将缓冲的响应内容写入实际响应
byte[] responseData = bufferedResponse.getResponseData();
if (responseData.length > 0) {
response.getOutputStream().write(responseData);
}
}
}

private void logRequestInfo(BufferedRequestWrapper request) throws IOException {
StringBuilder logMsg = new StringBuilder();
logMsg.append("REQUEST: ").append(request.getMethod())
.append(" ").append(request.getRequestURI())
.append("\nHeaders: ").append(getHeadersInfo(request))
.append("\nBody: ").append(request.getRequestBody());

logger.info(logMsg.toString());
}

private void logResponseInfo(HttpServletRequest request,
BufferedResponseWrapper response, long duration) {
StringBuilder logMsg = new StringBuilder();
logMsg.append("RESPONSE: ").append(request.getRequestURI())
.append(" - Status: ").append(response.getStatus())
.append(" - Duration: ").append(duration).append("ms")
.append("\nBody: ").append(response.getResponseContent());

logger.info(logMsg.toString());
}

private String getHeadersInfo(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
StringBuilder headers = new StringBuilder();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.append(headerName).append(": ")
.append(request.getHeader(headerName)).append("; ");
}
return headers.toString();
}

}
```


6. 最佳实践与注意事项


6.1 性能考虑



  • 避免在过滤器中执行耗时操作

  • 合理使用缓冲区,防止内存溢出

  • 考虑使用异步处理提高性能


6.2 异常处理


```java
public class ExceptionHandlingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
// 统一异常处理
handleException(e, (HttpServletResponse) response);
}
}


private void handleException(Exception e, HttpServletResponse response) {
// 实现异常处理逻辑
}

}
```


6.3 配置优化建议



  • 合理安排过滤器执行顺序

  • 使用@WebFilter注解简化配置

  • 考虑使用Spring Security等框架增强安全性


7. 总结


Servlet过滤器链和请求/响应包装器是Java Web开发中强大的工具,它们提供了非侵入式的请求/响应处理能力。通过合理运用这些技术,我们可以实现统一的安全控制、日志记录、性能监控等功能,同时保持代码的清晰和可维护性。


在实际项目中,建议根据具体需求选择合适的包装器策略,并注意性能优化和异常处理,以确保系统的稳定性和高效性。随着微服务架构的流行,这些技术在API网关、服务网格等场景中仍有重要的应用价值。


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值