Spring Boot中自定义AuthenticationFilter与permitAll冲突问题排查
问题分析与解决方案
核心概念错误
你混淆了过滤器执行时机和授权规则执行时机的顺序:
- Spring Security的过滤器链是在授权规则之前执行的。只要请求匹配了当前
SecurityFilterChain的securityMatcher("/api/v1/**")范围,不管后续配置了permitAll,自定义的JwtTokenAuthenticationFilter都会被触发执行。 - 你的
/api/v1/test/hola属于/api/v1/**的匹配范围,所以过滤器会先拦截该请求并执行认证逻辑,导致未授权问题,而授权规则的permitAll是在过滤器执行之后才生效的,根本没机会跳过认证。
实现自定义过滤器仅应用于部分端点的方案
方案1:在自定义过滤器内部添加跳过逻辑
直接在JwtTokenAuthenticationFilter的doFilter方法中,判断请求路径是否属于无需认证的范围,若是则直接放行,不执行认证逻辑:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String requestUri = httpRequest.getRequestURI(); // 跳过指定端点的认证逻辑 if ("/api/v1/test/hola".equals(requestUri)) { chain.doFilter(request, response); return; } // 原有认证逻辑 super.doFilter(request, response, chain); }
方案2:拆分多个SecurityFilterChain
创建两条独立的过滤器链,分别处理不同的请求范围:
- 优先级更高的链(Order(1))专门处理无需认证的端点,不添加自定义过滤器:
@Bean @Order(1) public SecurityFilterChain publicEndpointFilterChain(HttpSecurity http) throws Exception { return http.securityMatcher("/api/v1/test/hola") .csrf(Customizer.withDefaults()) .authorizeHttpRequests(r -> r.anyRequest().permitAll()) .build(); }
- 原有链(Order(2))处理剩余的
/api/v1/**端点,添加自定义过滤器:
@Bean @Order(2) public SecurityFilterChain generalFilterChain(HttpSecurity http) throws Exception { JwtTokenAuthenticationFilter jwtFilter = jwtTokenAuthenticationFilter(); jwtFilter.setAuthenticationManager(authenticationManager); return http.securityMatcher("/api/v1/**") .csrf(Customizer.withDefaults()) .authenticationManager(authenticationManager) .authorizeHttpRequests(r -> r.anyRequest().authenticated()) .addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class) .build(); }
注意:Spring Security会按
@Order的优先级匹配第一个符合条件的过滤器链,所以优先级高的链要先匹配特定端点。
方案3:给自定义过滤器设置RequestMatcher
通过RequestMatcher精确控制过滤器的应用范围,排除无需认证的端点:
public JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter() { JwtTokenAuthenticationFilter filter = new JwtTokenAuthenticationFilter(APPLICATION_GENERAL_PATH); filter.setAuthenticationManager(authenticationManager); // 设置过滤器仅匹配/api/v1/**,但排除/api/v1/test/hola RequestMatcher mainMatcher = new AntPathRequestMatcher("/api/v1/**"); RequestMatcher excludedMatcher = new NegatedRequestMatcher(new AntPathRequestMatcher("/api/v1/test/hola")); filter.setRequestMatcher(new AndRequestMatcher(mainMatcher, excludedMatcher)); return filter; }
内容的提问来源于stack exchange,提问作者Filip Cacic
相关产品推荐
相关产品推荐

