溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBoot中過濾器Filter怎么用

發布時間:2021-09-08 15:21:03 來源:億速云 閱讀:208 作者:小新 欄目:大數據
# 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銷毀...");
    }
}

2.2 注冊過濾器

在Spring Boot中有多種方式注冊過濾器,以下是三種常見方式:

方式1:使用@WebFilter注解 + @ServletComponentScan

@WebFilter(urlPatterns = "/*")
public class AnnotationFilter implements Filter {
    // 過濾器實現
}

// 在啟動類上添加掃描注解
@ServletComponentScan
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

方式2:通過FilterRegistrationBean注冊

@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;
    }
}

方式3:使用@Component自動注冊(不推薦)

@Component
public class ComponentFilter implements Filter {
    // 過濾器實現
}

注意:使用@Component注冊的過濾器無法自定義URL模式,會默認過濾所有請求,且順序難以控制。

2.3 過濾器執行順序控制

當應用中有多個過濾器時,執行順序非常重要。在Spring Boot中控制過濾器順序的方式:

  1. @WebFilter方式:通過實現javax.servlet.annotation.WebFilterfilterName屬性按字母順序執行
  2. FilterRegistrationBean方式:通過setOrder()方法設置順序值,值越小優先級越高
  3. @Order注解:對@Component過濾器有效,但無法控制URL模式

最佳實踐是使用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;
    }
}

三、過濾器的核心應用場景

3.1 請求日志記錄

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(", "));
    }
}

3.2 認證與授權檢查

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 ");
    }
}

3.3 跨域處理(CORS)

雖然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);
        }
    }
}

3.4 請求參數處理

統一字符編碼

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);
    }
}

XSS防護

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防護邏輯
    }
}

四、高級過濾器應用技巧

4.1 過濾器異常處理

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();
    }
}

4.2 性能監控過濾器

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();
        }
    }
}

4.3 請求/響應內容修改

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();
    }
}

4.4 與Spring Security集成

當同時使用過濾器和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;
    }
}

五、常見問題與最佳實踐

5.1 常見問題排查

問題1:過濾器不生效

  • 可能原因:未正確注冊過濾器
  • 解決方案:
    1. 檢查是否添加了@ServletComponentScan(對于@WebFilter)
    2. 檢查FilterRegistrationBean是否被@Configuration類加載
    3. 檢查過濾器URL模式是否匹配目標請求

問題2:過濾器執行順序不符合預期

  • 可能原因:多個過濾器順序配置沖突
  • 解決方案:
    1. 統一使用FilterRegistrationBean設置order
    2. 避免混合使用不同的注冊方式

問題3:性能問題

  • 可能原因:過濾器鏈過長或單個過濾器處理耗時
  • 解決方案:
    1. 優化過濾器邏輯,減少不必要的操作
    2. 考慮使用異步處理
    3. 對靜態資源使用不同的過濾器鏈

5.2 性能優化建議

  1. 減少過濾器數量:合并功能相似的過濾器
  2. 使用條件過濾:對靜態資源跳過某些過濾器
  3. 異步處理:對于耗時操作考慮使用異步過濾器
  4. 緩存策略:在過濾器中合理使用緩存
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();
    }
}

5.3 測試策略

單元測試

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網關過濾器

6.1 需求分析

假設我們需要實現一個API網關過濾器,需要處理以下功能: 1. 請求認證 2. 限流控制 3. 請求/響應日志 4. 耗時統計

6.2 實現代碼

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();
        }
    }
    
    // 其他輔助方法...
}

6.3 配置與使用

@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;
    }
}

七、總結與展望

7.1 關鍵點回顧

  1. 過濾器基礎:理解過濾器的生命周期和工作原理
  2. 注冊方式:掌握@WebFilter、FilterRegistrationBean等注冊方式及適用場景
  3. 執行順序:熟練控制多個過濾器的執行順序
  4. 應用場景:靈活運用過濾器解決實際問題

7.2 發展趨勢

隨著技術的演進,過濾器在以下方面有新的發展: 1. 云原生適配:與Service Mesh集成

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女