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

Spring OAuth2客户端授权码交换失败,求排查修复方案

问题分析

你遇到的authorization_request_not_found错误,本质是Spring Security OAuth2 Client在处理授权码回调时,无法找到发起授权请求时生成并存储的state参数。这个state用于防止CSRF攻击,默认存储在HttpSession中,一旦会话未创建或存储的state丢失,就会触发该错误。

结合你的配置,核心问题是Security规则允许所有请求无需认证,导致会话未被创建:当访问/hello触发授权流程时,由于permitAll()的设置,Spring Security不会自动创建会话,state无法存入会话;授权服务器回调后,客户端找不到对应的state,因此抛出错误并二次重定向到授权服务器。

排查步骤
  1. 确认错误触发点:查看OAuth2LoginAuthenticationFilter的日志,确认是在加载授权请求时返回null导致的错误,这直接指向state存储丢失。
  2. 检查会话状态:在授权流程前后打印会话ID,发现访问/hello时无会话创建,证明state没有存储的载体。
  3. 验证redirectUri配置:你的redirectUri设置与默认回调端点/login/oauth2/code/main-client一致,这部分无问题;但使用占位符{baseUrl}/login/oauth2/code/{registrationId}会更灵活,避免硬编码。
  4. 检查OAuth2AuthorizationRequestRepository:默认的HttpSessionOAuth2AuthorizationRequestRepository依赖HttpSession,若会话未创建则无法工作。
修复方案

以下是三种可行的修复方式,按推荐程度排序:

方案一:强制创建会话(最直接)

修改Security配置,设置会话创建策略为ALWAYS,确保所有请求都生成会话,让state有存储载体:

@Configuration
public class SecurityConfig {
    
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        
        http.oauth2Client(withDefaults())
            .oauth2Login(oauth2Login ->
                oauth2Login.loginPage("/oauth2/authorization/main-client")
            )
            // 强制创建会话
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
            );
        
        http.authorizeHttpRequests(authorize ->
                authorize.anyRequest().permitAll()
            );
        
        return http.build();
    }
}

方案二:规范redirectUri配置(更灵活)

将ClientRegistration中的redirectUri改为占位符格式,让Spring自动适配应用的基础URL,避免硬编码带来的潜在问题:

@Bean
public ClientRegistration mainWebClient() {
    
    return ClientRegistration
        .withRegistrationId("main-client")
        .clientId("test_web_client")
        .clientName("test_web_client")
        .clientSecret("secret")
        .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
        .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
        .scope(OidcScopes.OPENID)
        .authorizationUri("http://localhost:8080/oauth2/authorize")
        .tokenUri("http://localhost:8080/oauth2/token")
        // 使用占位符替代硬编码
        .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
        .build();
}

方案三:切换到基于服务的授权客户端管理

将OAuth2AuthorizedClientManager的构造参数改为OAuth2AuthorizedClientService,授权信息会存储在服务端而非会话(适合分布式场景):

@Bean
public OAuth2AuthorizedClientManager oauth2AuthorizedClientManager(
    ClientRegistrationRepository clientRegistrationRepository,
    OAuth2AuthorizedClientService authorizedClientService) {
    
    var clientManager =
        new DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository,
            authorizedClientService
        );
    
    clientManager
        .setAuthorizedClientProvider(
            OAuth2AuthorizedClientProviderBuilder.builder()
                .authorizationCode()
                .refreshToken()
                .build()
        );
    
    return clientManager;
}
验证方法

修复后,访问http://localhost:7070/hello:

  1. 会自动重定向到授权服务器登录页面
  2. 登录后授权服务器回调到客户端,成功获取access_token并返回
  3. 日志中不再出现authorization_request_not_found错误

内容的提问来源于stack exchange,提问作者Inquisitive

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:22:06