Spring Boot3+Security6无法放行指定URL的问题排查
Spring Boot 3 + Spring Security 6:放行/health等URL仍返回401的解决方案
我在开发Spring Boot 3项目时,集成Spring Security 6实现基于请求头Authorization Token的授权逻辑,希望让/health、/signup等URL无需Token即可访问,但目前访问这些路径时返回401,携带Token则正常返回200。
我的SecurityConfig和JwtFilter代码如下:
现有代码
SecurityConfiguration
@Configuration @EnableWebSecurity @Order(1) class SecurityConfiguration(private val jwtFilter : JwtFilter) { @Bean fun filterChain(http: HttpSecurity): SecurityFilterChain { http.csrf().disable(). authorizeHttpRequests { authorize -> authorize .requestMatchers("/health").permitAll() .anyRequest().authenticated() } http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter::class.java) return http.build() } }
JwtFilter
@Component class JwtFilter : Filter { override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { val httpRequest = request as HttpServletRequest val httpResponse = response as HttpServletResponse val jwt = httpRequest.getHeader("Authorization")?.removePrefix("Bearer ") if(jwt != null){ val extractedToken = verifyJwtWithLambda(jwt) val isValid : Boolean? = extractedToken["isValid"] as? Boolean if(isValid!!){ val attributes = extractedToken["attributes"] as? Map<*, *> val subject = attributes?.get("userId") as? String val authentication = UsernamePasswordAuthenticationToken(subject, null, emptyList()) SecurityContextHolder.getContext().authentication = authentication chain.doFilter(request, response) } else { httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED } } else { httpResponse.status = HttpServletResponse.SC_UNAUTHORIZED } } }
我尝试过添加WebSecurityCustomizer忽略URL、用.anonymous替代.permitAll等方案,都无效;在JwtFilter里加路径判断虽能解决,但不够优雅。
问题根源
你的JwtFilter直接实现了Servlet标准的Filter接口,它由Servlet容器管理,执行优先级高于Spring Security的过滤器链。也就是说,Spring Security配置的permitAll还没生效,自定义过滤器就已经拦截了请求,发现没有Authorization头就直接返回401了。
解决方案:改用Spring Security的OncePerRequestFilter
Spring Security提供了OncePerRequestFilter,专门用于在每个请求中执行一次过滤逻辑,并且可以通过shouldNotFilter方法指定不需要过滤的路径,完美适配你的需求。
1. 修改JwtFilter继承OncePerRequestFilter
@Component class JwtFilter : OncePerRequestFilter() { // 指定不需要过滤的路径 override fun shouldNotFilter(request: HttpServletRequest): Boolean { val path = request.servletPath return path == "/health" || path == "/signup" } override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain ) { val jwt = request.getHeader("Authorization")?.removePrefix("Bearer ") if(jwt != null){ val extractedToken = verifyJwtWithLambda(jwt) val isValid : Boolean? = extractedToken["isValid"] as? Boolean if(isValid!!){ val attributes = extractedToken["attributes"] as? Map<*, *> val subject = attributes?.get("userId") as? String val authentication = UsernamePasswordAuthenticationToken(subject, null, emptyList()) SecurityContextHolder.getContext().authentication = authentication filterChain.doFilter(request, response) } else { response.status = HttpServletResponse.SC_UNAUTHORIZED } } else { response.status = HttpServletResponse.SC_UNAUTHORIZED } } }
2. 调整SecurityConfig(优化配置)
移除不必要的@Order(1)(除非你有多个过滤器链需要排序),确保配置清晰:
@Configuration @EnableWebSecurity class SecurityConfiguration(private val jwtFilter : JwtFilter) { @Bean fun filterChain(http: HttpSecurity): SecurityFilterChain { http.csrf().disable() .authorizeHttpRequests { authorize -> authorize .requestMatchers("/health", "/signup").permitAll() .anyRequest().authenticated() } .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter::class.java) return http.build() } }
为什么之前的尝试无效?
- WebSecurityCustomizer忽略URL无效:WebSecurity的
ignoring()只会让路径跳过Spring Security过滤器链,但你的JwtFilter是Servlet容器管理的,不受此配置影响。 - .anonymous替代.permitAll无效:自定义过滤器先于Spring Security授权环节执行,还没到判断是否允许匿名访问的步骤就返回了401。
- addFilterBefore/After调整无效:无论调整在UsernamePasswordAuthenticationFilter的前后,自定义过滤器都会拦截所有请求,包括配置了
permitAll的路径。
内容的提问来源于stack exchange,提问作者nerdicsapo
相关产品推荐
相关产品推荐

