Spring Authorization Server+Angular SPA调用/oauth2/token返回403禁止访问
核心问题定位
日志明确显示客户端认证通过但请求授权被拒,说明问题出在令牌请求的权限校验环节,而非客户端身份验证。重点排查PKCE参数合法性、客户端配置、令牌端点权限规则这几个方向。
排查步骤及解决方案
1. 校验PKCE参数完整性与正确性
PKCE流程要求令牌请求必须携带code_verifier,且需和授权请求中的code_challenge/code_challenge_method严格匹配:
- 确认Angular请求(及Postman测试)中是否包含
code_verifier参数,值必须是授权请求时生成的原始随机字符串(未经过哈希编码) - 若授权请求用了
S256哈希算法,令牌请求无需传code_challenge_method,但code_verifier必须能通过S256哈希生成之前的code_challenge - Postman测试需手动模拟完整PKCE流程:先调用授权端点获取
code,再用对应的code_verifier调用令牌端点,禁止重复使用同一code(授权码仅能使用一次)
2. 检查客户端注册配置
确保客户端配置符合SPA+PKCE的要求:
- 客户端类型必须设为
PUBLIC,即配置clientSettings.setClientAuthenticationMethod(ClientAuthenticationMethod.NONE),因为SPA无法安全存储客户端密钥 - 必须启用PKCE:通过
clientSettings.setAuthorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)并添加OAuth2AuthorizationCodeGrantConfigurer::requireProofKey redirect_uri必须和Angular应用的回调地址完全匹配(包括协议、端口、路径,大小写敏感)- 令牌请求的
scope必须和授权请求一致,且是客户端已配置的权限范围
正确的客户端注册代码示例:
@Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient angularClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("angular-spa") .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("http://localhost:4200/callback") .scope(OidcScopes.OPENID) .scope(OidcScopes.PROFILE) .clientSettings(ClientSettings.builder() .requireAuthorizationConsent(false) .requireProofKey(true) // 强制启用PKCE .build()) .build(); return new InMemoryRegisteredClientRepository(angularClient); }
3. 确认安全配置的端点权限
确保令牌端点/oauth2/token允许匿名访问(PUBLIC客户端无需客户端认证):
- 若自定义了
authorizeHttpRequests规则,需显式对/oauth2/token设置permitAll,避免覆盖默认规则。示例配置:
@Bean public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults()); // 启用OIDC功能 http .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); // 显式允许令牌端点匿名访问 http.authorizeHttpRequests(auth -> auth .requestMatchers("/oauth2/token").permitAll() .anyRequest().authenticated()); return http.build(); }
4. 校验oidc-client-ts配置
确保Angular端的OIDC客户端配置正确:
response_type必须设为code(授权码模式)requirePkce设为trueclient_id、redirect_uri、scope需和认证服务器的客户端配置完全一致
示例配置:
import { UserManager, UserManagerSettings } from 'oidc-client-ts'; const settings: UserManagerSettings = { authority: 'http://localhost:8080', client_id: 'angular-spa', redirect_uri: 'http://localhost:4200/callback', response_type: 'code', scope: 'openid profile', requirePkce: true, loadUserInfo: true }; export const userManager = new UserManager(settings);
5. 深挖日志细节
从认证服务器日志中定位具体拒绝原因:
- 查找是否有
invalid_code_verifier、code_mismatch等关键字,这类信息直接指向PKCE参数错误 - 确认授权码
code未被重复使用,重复使用会直接触发403 - 检查请求的
grant_type是否为authorization_code,参数格式是否符合OAuth2规范
常见坑点
code_verifier长度必须在43-128字符之间,否则会被拒绝- 授权请求和令牌请求的客户端IP、用户代理等上下文不一致,可能被Spring Security的CSRF或会话保护拦截(SPA场景建议关闭令牌端点的CSRF校验)
- 认证服务器启用HTTPS但Angular应用用HTTP,会导致
redirect_uri不被允许(开发环境建议统一用HTTP,生产环境统一用HTTPS)
内容的提问来源于stack exchange,提问作者San Jaisy
相关产品推荐
相关产品推荐

