Spring 当 Spring 安全 OAuth 正在使用时 "serverWebExchange must be null" 启动 Reactive WebClient

Spring Boot Reactive WebClient "serverWebExchange must be null" when Spring Security OAuth is in use

我想使用 Spring Boot WebFlux 应用程序中的 WebClient,该应用程序使用 Spring Security OAuth 2 Client Credentials 设置。

但是,我得到:java.lang.IllegalArgumentException: serverWebExchange must be null

代码在这里:https://github.com/mparaz/spring-apigee-client

当我通过从 pom.xml 中删除它来禁用 Spring 安全性时,它可以正常工作。

当我继续使用 Spring 安全性,但不是将 webClient() 链结果返回给控制器,而是将其打印出来时,它也有效。

使用 Spring 安全性时,Reactive 客户端和服务器似乎无法协同工作。我怎样才能让他们 运行 在一起?

似乎如果您使用 UnAuthenticatedServerOAuth2AuthorizedClientRepository 它会将 webExchange 从源请求传播到您的 @RestController 到您用来调用其他服务的 WebClient导致 java.lang.IllegalArgumentException: serverWebExchange must be null

要解决此问题,请使用 ServerOAuth2AuthorizedClientRepository 的自动装配实现(恰好是 AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder(ReactiveClientRegistrationRepository clientRegistrations,
                                                          ObjectMapper objectMapper,
                                                          ServerOAuth2AuthorizedClientRepository clientRepository) {

        ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2ClientFilter = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
                clientRegistrations,
                clientRepository);
        oauth2ClientFilter.setDefaultClientRegistrationId("apigee");
        WebClient.Builder builder = WebClient.builder();
        builder.defaultHeader("Content-Type", MediaType.APPLICATION_JSON.toString());
        builder.defaultHeader("Accept", MediaType.APPLICATION_JSON.toString());
        builder.filter(oauth2ClientFilter);
        return builder;
    }

对我来说问题如下

https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#oauth2Client-authorized-manager-provider

The DefaultOAuth2AuthorizedClientManager is designed to be used within the context of a HttpServletRequest. When operating outside of a HttpServletRequest context, use AuthorizedClientServiceOAuth2AuthorizedClientManager instead.

对以下内容的评论 link 对我有用

https://www.gitmemory.com/issue/spring-projects/spring-security/8444/621567261

另一个link

https://github.com/spring-projects/spring-security/issues/8230

评论

DefaultReactiveOAuth2AuthorizedClientManager is intended to be used within a request context.

Given that you're seeing serverWebExchange cannot be null, you must be operating outside of a request context, which in case you should use AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager instead.

NOTE: Change the ServerOAuth2AuthorizedClientRepository parameter to ReactiveOAuth2AuthorizedClientService.

实际代码

    @Bean
    fun serverOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations: List<ClientRegistration>)
            : ServerOAuth2AuthorizedClientExchangeFilterFunction {
        val clientRegistrationRepository = InMemoryReactiveClientRegistrationRepository(clientRegistrations)
        val authorizedClientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)

        val oAuth2AuthorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
            clientRegistrationRepository,
            authorizedClientService
        )
        val filterFunction = ServerOAuth2AuthorizedClientExchangeFilterFunction(oAuth2AuthorizedClientManager)
        filterFunction.setDefaultClientRegistrationId(clientId)
        return filterFunction
    }