You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 21:45:37