Spring Security无Bearer Token时返回403而非401问题排查
Spring Security OAuth2资源服务器返回403而非401问题解决
问题概述
使用Spring Security OAuth2资源服务器做请求认证时,正常场景功能正常,但遇到两种情况会返回403而非预期的401:
- 请求中不存在Authorization请求头
- Authorization头的值不以Bearer开头
依赖版本
Spring Boot Starter - 2.6.7 Spring Boot Starter Security - 2.6.7 Spring Security Config & Web - 5.6.3 Spring Security Core - 5.3.19 Spring Boot Starter OAuth2 Resource Server - 2.6.7 Spring OAuth2 Resource Server - 5.6.3
尝试过的配置
曾添加BearerTokenAuthenticationEntryPoint相关代码,但由于使用的是令牌 introspection(而非JWT),这段代码仅在Bearer Token存在时生效,未解决无Token或Token格式错误时返回403的问题,当前配置代码如下:
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class CustomResourceServerSecurityConfiguration { @Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}") String introspectionUri; @Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}") String clientId; @Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}") String clientSecret; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2 .opaqueToken(opaque -> opaque.introspectionUri(this.introspectionUri) .introspectionClientCredentials(this.clientId, this.clientSecret)) .authenticationEntryPoint((request, response, exception) -> { System.out.println("Authentication failed"); BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint(); delegate.commence(request, response, exception); })) .exceptionHandling( (exceptions) -> exceptions.authenticationEntryPoint((request, response, exception) -> { System.out.println("Authentication is required"); BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint(); delegate.commence(request, response, exception); })); return http.build(); } }
问题原因
你重复配置了authenticationEntryPoint,导致逻辑冲突:
oauth2ResourceServer下的authenticationEntryPoint仅在令牌 introspection 失败(比如Token无效)时触发- 无Authorization头或头格式错误的场景,会被全局异常处理器拦截,但之前的重复配置没有正确覆盖默认行为,导致返回403
解决方案
移除oauth2ResourceServer内部的authenticationEntryPoint配置,统一在exceptionHandling中设置全局的BearerTokenAuthenticationEntryPoint,修改后的代码如下:
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class CustomResourceServerSecurityConfiguration { @Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}") String introspectionUri; @Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}") String clientId; @Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}") String clientSecret; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { BearerTokenAuthenticationEntryPoint bearerEntryPoint = new BearerTokenAuthenticationEntryPoint(); http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2 .opaqueToken(opaque -> opaque.introspectionUri(this.introspectionUri) .introspectionClientCredentials(this.clientId, this.clientSecret))) .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(bearerEntryPoint)); return http.build(); } }
原理说明
BearerTokenAuthenticationEntryPoint专门用于处理Bearer Token相关的未认证场景,会返回WWW-Authenticate: Bearer响应头和401状态码- 全局配置该入口点后,无Token、Token格式错误、Token introspection失败等所有未认证情况,都会统一返回401,符合OAuth2资源服务器的规范
内容的提问来源于stack exchange,提问作者iCode
相关产品推荐
相关产品推荐

