Spring Boot过滤器是Web应用程序中处理请求和响应的一种重要机制。过滤器能够在请求到达Servlet之前以及响应离开Servlet之后,对其进行预处理或后处理。这些处理可以包括日志记录、安全检查、数据压缩等。本文将详细介绍Spring Boot过滤器,从基础概念到高级应用,涵盖各类过滤器的使用场景、实现方法和最佳实践。
1. Spring Boot过滤器概述
1.1 过滤器的定义与作用
过滤器(Filter)是一种Java EE标准,允许在请求到达Servlet之前和响应离开Servlet之后对其进行处理。它们主要用于以下任务:
- 请求和响应的日志记录
- 安全性检查和认证
- 请求参数的修改和校验
- 响应的压缩和编码
1.2 过滤器的生命周期
过滤器的生命周期包括初始化(init)、处理(doFilter)和销毁(destroy)三个阶段:
- init:在过滤器实例被创建时调用,通常用于资源初始化。
- doFilter:在每次请求到达Servlet之前和响应离开Servlet之后调用,是主要的处理逻辑。
- destroy:在过滤器实例被销毁时调用,通常用于资源释放。
2. 创建Spring Boot过滤器
2.1 基础过滤器实现
在Spring Boot中创建一个过滤器需要实现javax.servlet.Filter接口,并重写其方法。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化逻辑
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("Request URI is: " + req.getRequestURI());
chain.doFilter(request, response); // 调用下一个过滤器或目标资源
}
@Override
public void destroy() {
// 资源释放逻辑
}
}
2.2 注册过滤器
在Spring Boot中,可以通过两种方式注册过滤器:使用@Bean注解或通过FilterRegistrationBean类。
使用@Bean注解注册过滤器
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> loggingFilter() {
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new MyFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
}
使用FilterRegistrationBean类注册过滤器
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyFilter> loggingFilter() {
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new MyFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(1); // 设置过滤器顺序
return registrationBean;
}
}
3. 常见过滤器类型
3.1 日志记录过滤器
日志记录过滤器用于记录请求和响应的详细信息,方便后续的分析和调试。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
System.out.println("Request URI: " + req.getRequestURI() + ", Time taken: " + (endTime - startTime) + " ms");
}
}
3.2 安全过滤器
安全过滤器用于检查请求的合法性,如验证用户身份、权限等。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SecurityFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String authHeader = req.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
// 验证token逻辑
String token = authHeader.substring(7);
if (!validateToken(token)) {
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
chain.doFilter(request, response);
}
private boolean validateToken(String token) {
// token验证逻辑
return "valid_token".equals(token);
}
}
3.3 压缩过滤器
压缩过滤器用于压缩响应数据,提高传输效率。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
public class CompressionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
CompressionResponseWrapper wrappedResponse = new CompressionResponseWrapper(res);
chain.doFilter(request, wrappedResponse);
wrappedResponse.finishResponse();
}
class CompressionResponseWrapper extends HttpServletResponseWrapper {
private GZIPOutputStream gzipOutputStream = null;
private ServletOutputStream servletOutputStream = null;
private PrintWriter printWriter = null;
public CompressionResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (servletOutputStream == null) {
servletOutputStream = createOutputStream();
}
return servletOutputStream;
}
@Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
printWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), getCharacterEncoding()));
}
return printWriter;
}
private ServletOutputStream createOutputStream() throws IOException {
gzipOutputStream = new GZIPOutputStream(getResponse().getOutputStream());
return new ServletOutputStream() {
@Override
public void write(int b) throws IOException {
gzipOutputStream.write(b);
}
@Override
public void close() throws IOException {
gzipOutputStream.close();
}
@Override
public void flush() throws IOException {
gzipOutputStream.flush();
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
};
}
public void finishResponse() throws IOException {
if (printWriter != null) {
printWriter.close();
}
if (gzipOutputStream != null) {
gzipOutputStream.close();
}
}
}
}
4. 过滤器的高级应用
4.1 过滤器链
过滤器链(Filter Chain)是多个过滤器依次执行的过程。每个过滤器在调用chain.doFilter()方法时会将请求传递给下一个过滤器。
配置多个过滤器
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public FilterRegistrationBean<SecurityFilter> securityFilter() {
FilterRegistrationBean<SecurityFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SecurityFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(2);
return registrationBean;
}
}
4.2 异步过滤器
异步过滤器允许在非阻塞模式下处理请求,适用于高并发场景。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
public class AsyncFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain
)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
CompletableFuture.runAsync(() -> {
try {
// 异步处理逻辑
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
e.printStackTrace();
}
});
}
}
4.3 自定义过滤器顺序
通过设置过滤器的order属性,可以自定义过滤器的执行顺序。
示例代码
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(1); // LoggingFilter先执行
return registrationBean;
}
@Bean
public FilterRegistrationBean<SecurityFilter> securityFilter() {
FilterRegistrationBean<SecurityFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SecurityFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(2); // SecurityFilter后执行
return registrationBean;
}
}
5. 过滤器的测试
5.1 单元测试
通过Mock框架,如Mockito,可以对过滤器进行单元测试。
示例代码
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static org.mockito.Mockito.*;
public class LoggingFilterTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Mock
private FilterChain filterChain;
@Test
public void testDoFilter() throws IOException, ServletException {
MockitoAnnotations.initMocks(this);
LoggingFilter loggingFilter = new LoggingFilter();
when(request.getRequestURI()).thenReturn("/api/test");
loggingFilter.doFilter(request, response, filterChain);
verify(filterChain).doFilter(request, response);
verify(request).getRequestURI();
}
}
5.2 集成测试
通过Spring Boot的测试框架,可以对过滤器进行集成测试。
示例代码
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FilterIntegrationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testFilters() {
String url = "http://localhost:" + port + "/api/test";
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
// 其他断言逻辑
}
}
6. 过滤器的性能优化
6.1 减少不必要的过滤器处理
确保每个过滤器只处理它关心的请求路径,避免不必要的处理。
示例代码
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/api/log/*"); // 仅处理 /api/log/ 路径
registrationBean.setOrder(1);
return registrationBean;
}
}
6.2 异步处理
使用异步过滤器处理耗时操作,减少对请求处理的阻塞。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
public class AsyncFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
CompletableFuture.runAsync(() -> {
try {
// 异步处理逻辑
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
e.printStackTrace();
}
});
}
}
6.3 缓存结果
对于一些固定响应内容的过滤器,可以使用缓存来提升性能。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class CachingFilter implements Filter {
private final Map<String, String> cache = new HashMap<>();
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String requestUri = req.getRequestURI();
if (cache.containsKey(requestUri)) {
res.getWriter().write(cache.get(requestUri));
} else {
CachingResponseWrapper responseWrapper = new CachingResponseWrapper(res);
chain.doFilter(request, responseWrapper);
String content = responseWrapper.getContent();
cache.put(requestUri, content);
res.getWriter().write(content);
}
}
class CachingResponseWrapper extends HttpServletResponseWrapper {
private final StringWriter sw = new StringWriter();
public CachingResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(sw);
}
public String getContent() {
return sw.toString();
}
}
}
7. 过滤器的安全性
7.1 防止XSS攻击
使用过滤器对请求参数进行校验,防止XSS攻击。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class XSSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
XSSRequestWrapper wrappedRequest = new XSSRequestWrapper(req);
chain.doFilter(wrappedRequest, response);
}
}
class XSSRequestWrapper extends HttpServletRequestWrapper {
public XSSRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanXSS(value);
}
private String cleanXSS(String value) {
if (value != null) {
value = value.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
value = value.replaceAll("'", "'");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
}
return value;
}
}
7.2 防止CSRF攻击
通过过滤器添加CSRF令牌,防止CSRF攻击。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CSRFTokenFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String csrfToken = (String) req.getSession().getAttribute("CSRF_TOKEN");
if (csrfToken == null) {
csrfToken = generateCSRFToken();
req.getSession().setAttribute("CSRF_TOKEN", csrfToken);
}
res.addHeader("CSRF-Token", csrfToken);
chain.doFilter(request, response);
}
private String generateCSRFToken() {
return Long.toHexString(System.currentTimeMillis());
}
}
7.3 防止SQL注入
通过过滤器对请求参数进行校验,防止SQL注入攻击。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SQLInjectionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest
) request;
HttpServletResponse res = (HttpServletResponse) response;
SQLInjectionRequestWrapper wrappedRequest = new SQLInjectionRequestWrapper(req);
chain.doFilter(wrappedRequest, response);
}
}
class SQLInjectionRequestWrapper extends HttpServletRequestWrapper {
public SQLInjectionRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanSQLInjection(value);
}
private String cleanSQLInjection(String value) {
if (value != null) {
value = value.replaceAll("'", "''");
value = value.replaceAll(";", "");
value = value.replaceAll("--", "");
}
return value;
}
}
8. 过滤器的监控和日志
8.1 记录过滤器的执行时间
通过日志记录过滤器的执行时间,便于性能监控和调试。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class ExecutionTimeLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
System.out.println("Request URI: " + req.getRequestURI() + ", Execution time: " + (endTime - startTime) + " ms");
}
}
8.2 记录请求和响应的详细信息
通过日志记录请求和响应的详细信息,便于分析和调试。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RequestResponseLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println("Request URI: " + req.getRequestURI());
System.out.println("Request Method: " + req.getMethod());
System.out.println("Request Headers: " + getRequestHeaders(req));
chain.doFilter(request, response);
System.out.println("Response Status: " + res.getStatus());
}
private String getRequestHeaders(HttpServletRequest request) {
StringBuilder headers = new StringBuilder();
request.getHeaderNames().asIterator().forEachRemaining(headerName -> {
headers.append(headerName).append(": ").append(request.getHeader(headerName)).append("\n");
});
return headers.toString();
}
}
9. 过滤器的故障处理
9.1 捕获和处理异常
通过过滤器捕获和处理请求中的异常,提供统一的错误响应。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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) {
HttpServletResponse res = (HttpServletResponse) response;
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
res.getWriter().write("Internal Server Error: " + e.getMessage());
}
}
}
9.2 自定义错误页面
通过过滤器实现自定义错误页面,提升用户体验。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomErrorPageFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect("/error-page");
}
}
}
10. 过滤器的最佳实践
10.1 避免长时间阻塞
避免在过滤器中执行长时间阻塞操作,如IO操作或复杂计算。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class NonBlockingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
new Thread(() -> {
try {
// 异步处理
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}).start();
}
}
10.2 合理配置过滤器顺序
根据业务逻辑合理配置过滤器的顺序,确保正确的执行顺序。
示例代码
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(1); // LoggingFilter先执行
return registrationBean;
}
@Bean
public FilterRegistrationBean<SecurityFilter> securityFilter() {
FilterRegistrationBean<SecurityFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SecurityFilter());
registrationBean.addUrlPatterns("/api/*");
registrationBean.setOrder(2); // SecurityFilter后执行
return registrationBean;
}
}
10.3 使用过滤器链
通过FilterChain有效地组织和管理多个过滤器。
示例代码
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CombinedFilter implements Filter {
private final Filter[] filters;
public CombinedFilter(Filter... filters) {
this.filters = filters;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
new FilterChain() {
private int index = 0;
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (index < filters.length) {
filters[index++].doFilter(request, response, this);
} else {
chain.doFilter(request, response);
}
}
}.doFilter(request, response);
}
}
总结
本文详细介绍了Spring Boot过滤器的各个方面,从基础概念到高级应用,涵盖了创建、注册、常见类型、性能优化、安全性、监控、故障处理及最佳实践等内容。通过这些知识,开发者可以更加灵活和高效地处理Web应用程序中的请求和响应,提升应用的性能和安全性。希望这篇文章能帮助您更好地理解和使用Spring Boot过滤器,提高开发效率和代码质量。
在实际开发中,掌握过滤器的使用不仅能够提高代码的可维护性,还能使您的Spring Boot应用更加健壮和灵活。今后,希望大家能多多尝试和应用这些过滤器,并不断总结经验,提升自己的开发技能。