# SpringBoot中過濾器Filter怎么用
## 一、過濾器(Filter)基礎概念
### 1.1 什么是過濾器
過濾器(Filter)是Java Web開發中的核心組件之一,它可以在請求到達Servlet之前或響應返回客戶端之前對HTTP請求和響應進行預處理和后處理。過濾器就像一個"篩子",能夠對Web應用中的請求和響應進行過濾操作。
在Spring Boot中,過濾器依然扮演著重要角色,盡管Spring提供了更高級的攔截器(Interceptor)機制,但過濾器在以下場景中仍不可替代:
1. 與Servlet容器緊密相關的功能(如編碼設置)
2. 需要處理靜態資源的場景
3. 在Spring上下文之外進行的處理
### 1.2 過濾器的工作原理
過濾器基于責任鏈模式工作,多個過濾器可以形成一個過濾鏈(FilterChain)。當一個請求到達時,過濾器的執行流程如下:
客戶端請求 → 過濾器1 → 過濾器2 → … → Servlet → 過濾器2 → 過濾器1 → 客戶端響應
關鍵特點:
- 先配置的過濾器先執行(請求階段)
- 后配置的過濾器后執行(響應階段)
- 可以通過FilterChain控制是否繼續執行下一個過濾器
### 1.3 過濾器與攔截器的區別
| 特性 | 過濾器(Filter) | 攔截器(Interceptor) |
|------------|------------------------|------------------------|
| 作用范圍 | Servlet容器級別 | Spring MVC上下文級別 |
| 依賴關系 | 不依賴Spring框架 | 依賴Spring MVC框架 |
| 實現方式 | 實現javax.servlet.Filter| 實現HandlerInterceptor |
| 執行時機 | 更早(進入Servlet前) | 稍晚(進入Controller前) |
| 使用場景 | 編碼轉換、跨域處理等 | 權限校驗、日志記錄等 |
## 二、Spring Boot中過濾器的基本使用
### 2.1 創建基礎過濾器
在Spring Boot中創建一個過濾器非常簡單,只需要實現`javax.servlet.Filter`接口:
```java
import javax.servlet.*;
import java.io.IOException;
public class BasicFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化方法
System.out.println("BasicFilter初始化...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("BasicFilter 前置處理");
// 執行后續過濾器或Servlet
chain.doFilter(request, response);
System.out.println("BasicFilter 后置處理");
}
@Override
public void destroy() {
// 銷毀方法
System.out.println("BasicFilter銷毀...");
}
}
在Spring Boot中有多種方式注冊過濾器,以下是三種常見方式:
@WebFilter(urlPatterns = "/*")
public class AnnotationFilter implements Filter {
// 過濾器實現
}
// 在啟動類上添加掃描注解
@ServletComponentScan
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<BasicFilter> registerBasicFilter() {
FilterRegistrationBean<BasicFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new BasicFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1); // 設置過濾器順序
registration.setName("basicFilter");
return registration;
}
}
@Component
public class ComponentFilter implements Filter {
// 過濾器實現
}
注意:使用@Component注冊的過濾器無法自定義URL模式,會默認過濾所有請求,且順序難以控制。
當應用中有多個過濾器時,執行順序非常重要。在Spring Boot中控制過濾器順序的方式:
javax.servlet.annotation.WebFilter
的filterName
屬性按字母順序執行setOrder()
方法設置順序值,值越小優先級越高最佳實踐是使用FilterRegistrationBean,因為它提供了最靈活的控制:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<FirstFilter> firstFilter() {
FilterRegistrationBean<FirstFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new FirstFilter());
bean.addUrlPatterns("/*");
bean.setOrder(1); // 第一個執行
return bean;
}
@Bean
public FilterRegistrationBean<SecondFilter> secondFilter() {
FilterRegistrationBean<SecondFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new SecondFilter());
bean.addUrlPatterns("/*");
bean.setOrder(2); // 第二個執行
return bean;
}
}
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 req = (HttpServletRequest) request;
long startTime = System.currentTimeMillis();
// 記錄請求信息
logger.info("請求開始: {} {}, 參數: {}",
req.getMethod(),
req.getRequestURI(),
getRequestParams(req));
chain.doFilter(request, response);
long duration = System.currentTimeMillis() - startTime;
logger.info("請求完成: 耗時{}ms", duration);
}
private String getRequestParams(HttpServletRequest req) {
return req.getParameterMap().entrySet().stream()
.map(entry -> entry.getKey() + "=" + Arrays.toString(entry.getValue()))
.collect(Collectors.joining(", "));
}
}
public class AuthFilter implements Filter {
private static final List<String> WHITE_LIST = Arrays.asList("/login", "/public");
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 白名單檢查
if (WHITE_LIST.contains(req.getRequestURI())) {
chain.doFilter(request, response);
return;
}
// 檢查認證token
String token = req.getHeader("Authorization");
if (!validateToken(token)) {
res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "無效的認證信息");
return;
}
chain.doFilter(request, response);
}
private boolean validateToken(String token) {
// 實際項目中應實現完整的token驗證邏輯
return token != null && token.startsWith("Bearer ");
}
}
雖然Spring Boot提供了@CrossOrigin
注解和全局CORS配置,但有時仍需要使用過濾器處理:
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) request).getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(request, response);
}
}
}
public class EncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void init(FilterConfig filterConfig) {
String encodingParam = filterConfig.getInitParameter("encoding");
if (encodingParam != null) {
encoding = encodingParam;
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
}
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new XssRequestWrapper((HttpServletRequest) request), response);
}
private static class XssRequestWrapper extends HttpServletRequestWrapper {
// 實現XSS防護邏輯
}
}
public class ExceptionHandlingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (BusinessException e) {
handleBusinessException((HttpServletResponse) response, e);
} catch (Exception e) {
handleUnexpectedException((HttpServletResponse) response, e);
}
}
private void handleBusinessException(HttpServletResponse response,
BusinessException e) throws IOException {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("{\"error\":\"" + e.getMessage() + "\"}");
response.getWriter().flush();
}
private void handleUnexpectedException(HttpServletResponse response,
Exception e) throws IOException {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("{\"error\":\"系統異常\"}");
response.getWriter().flush();
}
}
public class MetricsFilter implements Filter {
private final MetricRegistry metricRegistry;
public MetricsFilter(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String metricName = "request." + req.getMethod() + "." + req.getRequestURI()
.replaceAll("/", ".");
Timer.Context timerContext = metricRegistry.timer(metricName).time();
try {
chain.doFilter(request, response);
} finally {
timerContext.stop();
}
}
}
public class ContentModificationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ContentCachingResponseWrapper responseWrapper =
new ContentCachingResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
byte[] content = responseWrapper.getContentAsByteArray();
if (content.length > 0) {
String contentStr = new String(content, response.getCharacterEncoding());
// 修改響應內容
String modifiedContent = contentStr.replace("old", "new");
responseWrapper.resetBuffer();
responseWrapper.getWriter().write(modifiedContent);
}
responseWrapper.copyBodyToResponse();
}
}
當同時使用過濾器和Spring Security時,需要注意執行順序:
@Configuration
public class SecurityFilterConfig {
@Bean
public FilterRegistrationBean<CustomSecurityFilter> securityFilterRegistration() {
FilterRegistrationBean<CustomSecurityFilter> registration =
new FilterRegistrationBean<>();
registration.setFilter(new CustomSecurityFilter());
registration.addUrlPatterns("/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); // 在Spring Security之前執行
return registration;
}
}
public class CacheFilter implements Filter {
private CacheManager cacheManager;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String cacheKey = generateCacheKey(req);
// 檢查緩存
if (cacheManager.get(cacheKey) != null) {
writeCachedResponse(response, cacheManager.get(cacheKey));
return;
}
// 緩存響應
ContentCachingResponseWrapper responseWrapper =
new ContentCachingResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
byte[] responseData = responseWrapper.getContentAsByteArray();
cacheManager.put(cacheKey, responseData);
responseWrapper.copyBodyToResponse();
}
}
public class AuthFilterTest {
@Test
public void testWhiteListAccess() throws Exception {
AuthFilter filter = new AuthFilter();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
request.setRequestURI("/login");
filter.doFilter(request, response, chain);
verify(chain).doFilter(request, response);
}
}
@SpringBootTest
@AutoConfigureMockMvc
public class FilterIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testAuthFilterWithValidToken() throws Exception {
mockMvc.perform(get("/api/resource")
.header("Authorization", "Bearer valid-token"))
.andExpect(status().isOk());
}
@Test
public void testAuthFilterWithoutToken() throws Exception {
mockMvc.perform(get("/api/resource"))
.andExpect(status().isUnauthorized());
}
}
假設我們需要實現一個API網關過濾器,需要處理以下功能: 1. 請求認證 2. 限流控制 3. 請求/響應日志 4. 耗時統計
public class ApiGatewayFilter implements Filter {
private final RateLimiter rateLimiter;
private final Logger logger = LoggerFactory.getLogger(getClass());
public ApiGatewayFilter(RateLimiter rateLimiter) {
this.rateLimiter = rateLimiter;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// 1. 認證檢查
if (!authenticate(req)) {
sendError(res, HttpServletResponse.SC_UNAUTHORIZED, "認證失敗");
return;
}
// 2. 限流檢查
if (!rateLimiter.tryAcquire()) {
sendError(res, HttpServletResponse.SC_TOO_MANY_REQUESTS, "請求過于頻繁");
return;
}
// 3. 記錄請求日志
long startTime = System.currentTimeMillis();
logRequest(req);
// 4. 處理請求
ContentCachingResponseWrapper responseWrapper =
new ContentCachingResponseWrapper(res);
try {
chain.doFilter(request, responseWrapper);
} finally {
// 5. 記錄響應日志和耗時
long duration = System.currentTimeMillis() - startTime;
logResponse(req, responseWrapper, duration);
responseWrapper.copyBodyToResponse();
}
}
// 其他輔助方法...
}
@Configuration
public class ApiGatewayConfig {
@Bean
public FilterRegistrationBean<ApiGatewayFilter> apiGatewayFilter() {
FilterRegistrationBean<ApiGatewayFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new ApiGatewayFilter(new RedisRateLimiter()));
bean.addUrlPatterns("/api/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.setName("apiGatewayFilter");
return bean;
}
}
隨著技術的演進,過濾器在以下方面有新的發展: 1. 云原生適配:與Service Mesh集成
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。