Skip to content

过滤器链与执行顺序

过滤器顺序本身就是机制的一部分。Spring Security 大量问题都不是“配置错”,而是过滤器插错位置。

常见执行顺序

Servlet 场景下常见主链路可以概括为:

  1. SecurityContextHolderFilter
  2. HeaderWriterFilter
  3. CsrfFilter
  4. LogoutFilter
  5. UsernamePasswordAuthenticationFilter
  6. BasicAuthenticationFilter
  7. RequestCacheAwareFilter
  8. SecurityContextHolderAwareRequestFilter
  9. AnonymousAuthenticationFilter
  10. SessionManagementFilter
  11. ExceptionTranslationFilter
  12. AuthorizationFilter

不同功能开启后还会插入例如:

  • OAuth2AuthorizationRequestRedirectFilter
  • OAuth2LoginAuthenticationFilter
  • BearerTokenAuthenticationFilter

为什么是这个顺序

  • 上下文恢复必须最早,否则后续拿不到身份。
  • 认证要早于授权,否则 AuthorizationFilter 没法判断。
  • ExceptionTranslationFilter 要包住后面的授权异常。
  • 匿名认证要晚于真实认证,否则会把未登录请求过早固化成匿名主体。

顺序示意

text
request
  -> SecurityContextHolderFilter
  -> ...身份恢复/基础保护...
  -> Authentication Filter
  -> AnonymousAuthenticationFilter
  -> ExceptionTranslationFilter
  -> AuthorizationFilter
  -> controller

自定义 JWT 过滤器

java
@Component
class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String authorization = request.getHeader("Authorization");
        if (authorization != null && authorization.startsWith("Bearer ")) {
            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    "jwtUser", null, List.of(new SimpleGrantedAuthority("ROLE_USER")));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }
}
java
@Bean
SecurityFilterChain apiChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
    return http
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .build();
}

插入位置判断

  • JWT 解析通常放在 UsernamePasswordAuthenticationFilter 前面。
  • 审计日志过滤器常放在认证后、授权前。
  • 异常包装过滤器不能压过 ExceptionTranslationFilter 的职责。