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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化逻辑}@Overridepublic 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); // 调用下一个过滤器或目标资源}@Overridepublic 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;@Configurationpublic class FilterConfig {@Beanpublic 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;@Configurationpublic class FilterConfig {@Beanpublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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);}@Overridepublic ServletOutputStream getOutputStream() throws IOException {if (servletOutputStream == null) {servletOutputStream = createOutputStream();}return servletOutputStream;}@Overridepublic 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() {@Overridepublic void write(int b) throws IOException {gzipOutputStream.write(b);}@Overridepublic void close() throws IOException {gzipOutputStream.close();}@Overridepublic void flush() throws IOException {gzipOutputStream.flush();}@Overridepublic boolean isReady() {return false;}@Overridepublic 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()方法时会将请求传递给下一个过滤器。
配置多个过滤器
@Configurationpublic class FilterConfig {@Beanpublic FilterRegistrationBean<LoggingFilter> loggingFilter() {FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new LoggingFilter());registrationBean.addUrlPatterns("/api/*");registrationBean.setOrder(1);return registrationBean;}@Beanpublic 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 {@Overridepublic 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属性,可以自定义过滤器的执行顺序。
示例代码
@Configurationpublic class FilterConfig {@Beanpublic FilterRegistrationBean<LoggingFilter> loggingFilter() {FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new LoggingFilter());registrationBean.addUrlPatterns("/api/*");registrationBean.setOrder(1); // LoggingFilter先执行return registrationBean;}@Beanpublic 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 {@Mockprivate HttpServletRequest request;@Mockprivate HttpServletResponse response;@Mockprivate FilterChain filterChain;@Testpublic 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 {@LocalServerPortprivate int port;@Autowiredprivate TestRestTemplate restTemplate;@Testpublic 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 减少不必要的过滤器处理
确保每个过滤器只处理它关心的请求路径,避免不必要的处理。
示例代码
@Configurationpublic class FilterConfig {@Beanpublic 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 {@Overridepublic 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<>();@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 合理配置过滤器顺序
根据业务逻辑合理配置过滤器的顺序,确保正确的执行顺序。
示例代码
@Configurationpublic class FilterConfig {@Beanpublic FilterRegistrationBean<LoggingFilter> loggingFilter() {FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new LoggingFilter());registrationBean.addUrlPatterns("/api/*");registrationBean.setOrder(1); // LoggingFilter先执行return registrationBean;}@Beanpublic 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;}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {new FilterChain() {private int index = 0;@Overridepublic 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应用更加健壮和灵活。今后,希望大家能多多尝试和应用这些过滤器,并不断总结经验,提升自己的开发技能。
