Spring Boot 过滤器(Filter)详解
引言
Spring Boot过滤器是Servlet规范的重要组成部分,用于在请求到达目标资源之前或响应返回给客户端之后执行预处理或后处理逻辑。过滤器在Web应用的安全控制、日志记录、请求处理、响应处理等方面发挥着重要作用,是构建高质量Web应用的基础组件之一。
过滤器基础概念
过滤器的作用和原理
过滤器是Java Servlet规范中定义的组件,用于拦截请求和响应,可以在请求到达Servlet之前或响应返回给客户端之前执行特定逻辑。过滤器遵循责任链模式,多个过滤器可以组成过滤器链,按照配置顺序依次执行。
过滤器生命周期
过滤器具有完整的生命周期管理:
- init():过滤器初始化时调用
- doFilter():处理过滤逻辑的主要方法
- destroy():过滤器销毁时调用
过滤器实现方式
实现Filter接口
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 过滤器初始化逻辑
System.out.println("CustomFilter initialized");
String initParam = filterConfig.getInitParameter("paramName");
System.out.println("Init parameter: " + initParam);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 请求预处理
String requestURI = httpRequest.getRequestURI();
long startTime = System.currentTimeMillis();
System.out.println("Request started: " + requestURI);
try {
// 继续执行过滤器链
chain.doFilter(request, response);
} finally {
// 响应后处理
long duration = System.currentTimeMillis() - startTime;
System.out.println("Request completed: " + requestURI +
", Duration: " + duration + "ms");
}
}
@Override
public void destroy() {
// 过滤器销毁逻辑
System.out.println("CustomFilter destroyed");
}
}
使用@WebFilter注解
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
@WebFilter(
urlPatterns = {
"/api/*", "/admin/*"},
initParams = {
@WebInitParam(name = "paramName", value = "paramValue")
},
filterName = "customWebFilter"
)
public class WebFilterExample implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("WebFilterExample initialized");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("WebFilterExample processing request");
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("WebFilterExample destroyed");
}
}
Spring Boot中的过滤器配置
使用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<CustomFilter> customFilterRegistration() {
FilterRegistrationBean<CustomFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CustomFilter());
registration.addUrlPatterns("/api/*", "/user/*");
registration.setName("customFilter");
registration.setOrder(1); // 设置执行顺序
// 添加初始化参数
registration.addInitParameter("paramName", "paramValue");
return registration;
}
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilterRegistration() {
FilterRegistrationBean<LoggingFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new LoggingFilter());
registration.addUrlPatterns("/*");
registration.setName("loggingFilter");
registration.setOrder(2);
return registration;
}
}
使用@Component注解
import org.springframework.stereotype.Component;
@Component
public class ComponentFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String userAgent = httpRequest.getHeader("User-Agent");
System.out.println("User-Agent: " + userAgent);
chain.doFilter(request, response);
}
}
常见过滤器实现
日志过滤器
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String method = httpRequest.getMethod();
String uri = httpRequest.getRequestURI();
String queryString = httpRequest.getQueryString();
String clientIP = getClientIP(httpRequest);
long startTime = System.currentTimeMillis();
logger.info("Request: {} {}{} from {}", method, uri,
queryString != null ? "?" + queryString : "", clientIP);
try {
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
logger.info("Response: {} - Duration: {}ms",
httpResponse.getStatus(), duration);
}
}
private String getClientIP(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
String xRealIP = request.getHeader("X-Real-IP");
if (xRealIP != null && !xRealIP.isEmpty()) {
return xRealIP;
}
return request.getRemoteAddr();
}
}
CORS过滤器
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 设置CORS头
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers",
"Content-Type, Authorization, X-Requested-With");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
// 处理预检请求
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
认证过滤器
import java.util.Base64;
public class AuthenticationFilter implements Filter {
private static final String AUTH_HEADER = "Authorization";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String authHeader = httpRequest.getHeader(AUTH_HEADER);
if (authHeader != null && authHeader.startsWith("Basic ")) {
String credentials = authHeader.substring(6);
String decodedCredentials = new String(Base64.getDecoder().decode(credentials));
String[] parts = decodedCredentials.split(":");
if (parts.length == 2) {
String username = parts[0];
String password = parts[1];
if (validateCredentials(username, password)) {
// 设置用户信息到请求属性
httpRequest.setAttribute("user", username);
chain.doFilter(request, response);
return;
}
}
}
// 认证失败,返回401
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.getWriter().write("Authentication required");
}
private boolean validateCredentials(String username, String password) {
// 简单的认证逻辑,实际应用中应使用更安全的验证方式
return "admin".equals(username) && "password".equals(password);
}
}
请求参数过滤器
public class ParameterFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(httpRequest) {
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return sanitizeParameter(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values != null) {
for (int i = 0; i < values.length; i++) {
values[i] = sanitizeParameter(values[i]);
}
}
return values;
}
};
chain.doFilter(wrappedRequest, response);
}
private String sanitizeParameter(String value) {
if (value == null) {
return null;
}
// 简单的XSS防护
return value.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("\"", """)
.replaceAll("'", "'");
}
}
过滤器链管理
过滤器执行顺序
在Spring Boot中,过滤器的执行顺序非常重要,可以通过以下方式控制:
// 1. 使用FilterRegistrationBean的setOrder方法
@Bean
public FilterRegistrationBean<FirstFilter> firstFilter() {
FilterRegistrationBean<FirstFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new FirstFilter());
registration.setOrder(1); // 数字越小,优先级越高
return registration;
}
// 2. 实现Ordered接口
@Component
public class OrderedFilter implements Filter, Ordered {
@Override
public int getOrder() {
return 1; // 返回优先级数字
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("OrderedFilter executed");
chain.doFilter(request, response);
}
}
过滤器链调试
public class DebugFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(DebugFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String filterName = this.getClass().getSimpleName();
logger.debug("Filter {} - Before: {} {}", filterName,
httpRequest.getMethod(), httpRequest.getRequestURI());
chain.doFilter(request, response);
logger.debug("Filter {} - After: {} {}", filterName,
httpRequest.getMethod(), httpRequest.getRequestURI());
}
}
高级过滤器应用
性能监控过滤器
import java.util.concurrent.ConcurrentHashMap;
public class PerformanceMonitorFilter implements Filter {
private final Map<String, RequestStats> stats = new ConcurrentHashMap<>();
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String uri = httpRequest.getRequestURI();
long startTime = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
updateStats(uri, duration);
}
}
private void updateStats(String uri, long duration) {
stats.computeIfAbsent(uri, k -> new RequestStats())
.update(duration);
}
public void printStats() {
stats.forEach((uri, stat) -> {
System.out.printf("URI: %s, Avg: %.2fms, Min: %dms, Max: %dms%n",
uri, stat.getAverage(), stat.getMin(), stat.getMax());
});
}
private static class RequestStats {
private long totalDuration = 0;
private long requestCount = 0;
private long minDuration = Long.MAX_VALUE;
private long maxDuration = 0;
public synchronized void update(long duration) {
totalDuration += duration;
requestCount++;
minDuration = Math.min(minDuration, duration);
maxDuration = Math.max(maxDuration, duration);
}
public double getAverage() {
return requestCount > 0 ? (double) totalDuration / requestCount : 0;
}
public long getMin() {
return minDuration == Long.MAX_VALUE ? 0 : minDuration;
}
public long getMax() {
return maxDuration;
}
}
}
限流过滤器
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class RateLimitFilter implements Filter {
private final Map<String, AtomicInteger> requestCounters = new ConcurrentHashMap<>();
private final long windowSize = 60000; // 1分钟窗口
private final int maxRequests = 100; // 每分钟最大请求数
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String clientIP = getClientIP(httpRequest);
long currentTime = System.currentTimeMillis();
String key = clientIP + "_" + (currentTime / windowSize);
AtomicInteger counter = requestCounters.computeIfAbsent(key, k -> new AtomicInteger(0));
int currentCount = counter.incrementAndGet();
if (currentCount > maxRequests) {
httpResponse.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
httpResponse.getWriter().write("Rate limit exceeded");
return;
}
// 清理过期的计数器
cleanExpiredCounters(currentTime);
chain.doFilter(request, response);
}
private String getClientIP(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
private void cleanExpiredCounters(long currentTime) {
long cutoffTime = (currentTime / windowSize) - 2; // 保留2个窗口的数据
requestCounters.entrySet().removeIf(entry -> {
String[] parts = entry.getKey().split("_");
if (parts.length == 2) {
try {
long window = Long.parseLong(parts[1]);
return window < cutoffTime;
} catch (NumberFormatException e) {
return true;
}
}
return false;
});
}
}
缓存过滤器
public class CacheFilter implements Filter {
private final Map<String, CachedResponse> cache = new ConcurrentHashMap<>();
private final long cacheTimeout = 300000; // 5分钟
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String cacheKey = generateCacheKey(httpRequest);
// 检查缓存
CachedResponse cached = cache.get(cacheKey);
if (cached != null && !cached.isExpired()) {
// 返回缓存的响应
cached.writeTo(response);
return;
}
// 创建响应包装器来捕获响应内容
HttpServletResponse httpResponse = (HttpServletResponse) response;
CachedResponseWrapper responseWrapper = new CachedResponseWrapper(httpResponse);
chain.doFilter(request, responseWrapper);
// 缓存响应
if (httpResponse.getStatus() == 200) {
CachedResponse newCached = new CachedResponse(responseWrapper);
cache.put(cacheKey, newCached);
}
}
private String generateCacheKey(HttpServletRequest request) {
return request.getMethod() + "_" + request.getRequestURI() +
"_" + request.getQueryString();
}
private static class CachedResponse {
private final byte[] content;
private final int status;
private final Map<String, String> headers;
private final long timestamp;
public CachedResponse(CachedResponseWrapper response) {
this.content = response.getContentAsByteArray();
this.status = response.getStatus();
this.headers = new HashMap<>();
response.getHeaderNames().forEach(name ->
headers.put(name, response.getHeader(name)));
this.timestamp = System.currentTimeMillis();
}
public boolean isExpired() {
return System.currentTimeMillis() - timestamp > 300000; // 5分钟
}
public void writeTo(ServletResponse response) throws IOException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(status);
headers.forEach(httpResponse::setHeader);
httpResponse.getOutputStream().write(content);
}
}
private static class CachedResponseWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final PrintWriter writer = new PrintWriter(new OutputStreamWriter(buffer));
public CachedResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public PrintWriter getWriter() throws IOException {
return writer;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
@Override
public void write(int b) throws IOException {
buffer.write(b);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
// 不实现
}
};
}
public byte[] getContentAsByteArray() {
if (writer != null) {
writer.flush();
}
return buffer.toByteArray();
}
}
}
过滤器与Spring Security集成
Spring Security过滤器链
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public CustomAuthenticationFilter customAuthenticationFilter() {
return new CustomAuthenticationFilter();
}
}
自定义认证过滤器与Spring Security结合
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
try {
Claims claims = Jwts.parser()
.setSigningKey("secretKey")
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get("roles").toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
Authentication auth = new UsernamePasswordAuthenticationToken(
username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (Exception e) {
logger.error("JWT token validation failed", e);
}
}
filterChain.doFilter(request, response);
}
}
过滤器性能优化
异步过滤器
public class AsyncFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.isAsyncStarted()) {
// 异步请求处理
AsyncContext asyncContext = httpRequest.getAsyncContext();
asyncContext.start(() -> {
try {
// 异步处理逻辑
processAsyncRequest(asyncContext);
chain.doFilter(request, response);
} catch (Exception e) {
asyncContext.complete();
}
});
} else {
chain.doFilter(request, response);
}
}
private void processAsyncRequest(AsyncContext context) {
// 异步处理逻辑
System.out.println("Processing async request");
}
}
过滤器缓存优化
@Component
public class OptimizedFilter implements Filter {
private final LoadingCache<String, Boolean> permissionCache;
public OptimizedFilter() {
this.permissionCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10))
.build(this::checkPermission);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String resource = httpRequest.getRequestURI();
String user = (String) httpRequest.getAttribute("user");
if (user != null && permissionCache.get(resource)) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(403, "Access denied");
}
}
private Boolean checkPermission(String resource) {
// 权限检查逻辑
return true; // 简化示例
}
}
过滤器测试
单元测试
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class FilterTest {
@Test
public void testCustomFilter() throws Exception {
CustomFilter filter = new CustomFilter();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/api/test");
request.setMethod("GET");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain();
filter.doFilter(request, response, chain);
// 验证结果
assertEquals(200, response.getStatus());
}
}
集成测试
@SpringBootTest
@AutoConfigureTestDatabase
class FilterIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void testAuthenticationFilter() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Basic " + Base64.getEncoder()
.encodeToString("admin:password".getBytes()));
HttpEntity<String> entity = new HttpEntity<>("", headers);
ResponseEntity<String> response = restTemplate.exchange(
"/api/protected",
HttpMethod.GET,
entity,
String.class
);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
最佳实践和注意事项
性能考虑
- 避免在过滤器中执行耗时操作
- 使用缓存减少重复计算
- 合理设置过滤器执行顺序
- 及时清理资源
安全考虑
- 验证输入参数的安全性
- 防止敏感信息泄露
- 实现适当的错误处理
- 使用HTTPS传输
调试和监控
// 添加过滤器执行时间监控
public class MonitoringFilter implements Filter {
private final MeterRegistry meterRegistry;
public MonitoringFilter(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
Timer.Sample sample = Timer.start(meterRegistry);
try {
chain.doFilter(request, response);
} finally {
sample.stop(Timer.builder("filter.execution.time")
.tag("filter", this.getClass().getSimpleName())
.register(meterRegistry));
}
}
}
总结
Spring Boot过滤器是构建Web应用的重要组件,通过合理使用过滤器可以实现请求预处理、安全控制、日志记录、性能监控等多种功能。在实际开发中,需要根据具体需求选择合适的过滤器实现方式,并注意性能优化和安全考虑,以构建高质量的Web应用程序。
关于作者
🌟 我是suxiaoxiang,一位热爱技术的开发者
💡 专注于Java生态和前沿技术分享
🚀 持续输出高质量技术内容
如果这篇文章对你有帮助,请支持一下:
👍 点赞
⭐ 收藏
👀 关注
您的支持是我持续创作的动力!感谢每一位读者的关注与认可!