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

Spring 5 WebClient集成内置OAuth2安全特性时触发ClientAuthorizationException[server_error]问题排查

Spring 5 WebClient OAuth2内置特性调用报错org.springframework.security.oauth2.client.ClientAuthorizationException: [server_error]

问题描述

我正尝试使用Spring 5 WebClient及其内置的OAuth2安全特性,配置了如下的oauth2WebClient Bean:

@Bean("oauth2WebClient")
public WebClient oauth2WebClient(final ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager) {
    final ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(reactiveOAuth2AuthorizedClientManager);
    serverOAuth2AuthorizedClientExchangeFilterFunction.setDefaultClientRegistrationId("app");
    try {
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());

        // Set up key manager factory to use our key store
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, "password".toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);

        SslContext sslContext = SslContextBuilder.forClient()
                .keyManager(keyManagerFactory)
                .trustManager(trustManagerFactory)
                .build();

        HttpClient httpClient = HttpClient.create()
                .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
        ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
        return WebClient.builder()
                .filter(serverOAuth2AuthorizedClientExchangeFilterFunction)
                .clientConnector(connector)
                .build();
    } catch (Exception e) {
        // 建议补充异常处理逻辑
        throw new RuntimeException("Failed to configure OAuth2 WebClient with SSL", e);
    }
}

我已确认SSL证书配置正确,且当我手动使用WebClient获取AccessToken并调用服务时可以正常工作;但使用内置OAuth2库时,出现了如下错误:

org.springframework.security.oauth2.client.ClientAuthorizationException: [server_error]

堆栈跟踪信息如下:

reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to POST <url>[DefaultWebClient]
Stack trace:
java.lang.Exception: #block terminated with an error

请问我哪里操作出错了?


可能的原因与解决方案

1. 令牌请求未复用自定义SSL配置

你手动调用时能成功,是因为用了自己配置的带SSL上下文的WebClient;但Spring OAuth2内置的令牌获取请求是由ReactiveOAuth2AuthorizedClientManager发起的,它默认使用无自定义SSL配置的默认WebClient,这会导致令牌请求时SSL验证失败,最终抛出server_error。

解决方案:
给ReactiveOAuth2AuthorizedClientManager配置自定义的ReactiveOAuth2AccessTokenResponseClient,让它复用你的SSL配置来请求令牌:

@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        ReactiveOAuth2AuthorizedClientService authorizedClientService) throws Exception {

    // 复用你之前的SSL上下文配置
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());
    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    trustStore.load(new FileInputStream(new ClassPathResource("app.jks").getFile()), "password".toCharArray());

    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, "password".toCharArray());
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(trustStore);

    SslContext sslContext = SslContextBuilder.forClient()
            .keyManager(keyManagerFactory)
            .trustManager(trustManagerFactory)
            .build();

    // 构建用于令牌请求的带SSL的WebClient
    HttpClient tokenHttpClient = HttpClient.create()
            .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
    ClientHttpConnector tokenConnector = new ReactorClientHttpConnector(tokenHttpClient);
    WebClient tokenWebClient = WebClient.builder()
            .clientConnector(tokenConnector)
            .build();

    // 配置令牌响应客户端
    WebClientReactiveClientCredentialsTokenResponseClient tokenResponseClient =
            new WebClientReactiveClientCredentialsTokenResponseClient();
    tokenResponseClient.setWebClient(tokenWebClient);

    DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
            new DefaultReactiveOAuth2AuthorizedClientManager(
                    clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(
            ClientCredentialsReactiveOAuth2AuthorizedClientProvider.builder()
                    .accessTokenResponseClient(tokenResponseClient)
                    .build());

    return authorizedClientManager;
}

2. ReactiveOAuth2AuthorizedClientManager配置不完整

如果你的ReactiveOAuth2AuthorizedClientManager是默认自动配置的,它可能没有正确关联你的ClientRegistration(比如client-id为"app"的配置是否正确加载),或者缺少必要的授权提供者配置。

排查步骤:

  • 检查application.yml/application.properties中spring.security.oauth2.client.registration.app下的配置,确保token-uri、client-id、client-secret、scope等参数正确无误;
  • 确保自定义的authorizedClientManager明确指定了对应的授权提供者(比如客户端模式的ClientCredentialsReactiveOAuth2AuthorizedClientProvider)。

3. 详细异常信息被隐藏

server_error是比较笼统的错误,你可以开启Spring Security的DEBUG日志,查看令牌请求的详细错误信息:

logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.web.reactive.function.client=DEBUG

通过详细日志,你可以看到令牌请求的具体响应内容,比如是否是SSL握手失败、令牌端点返回了具体的错误信息等,这能帮你更快定位问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 13:18:12