使用 Spring Security OAuth 时 Spring Boot Reactive WebClient “serverWebExchange must be null”
Posted
技术标签:
【中文标题】使用 Spring Security OAuth 时 Spring Boot Reactive WebClient “serverWebExchange must be null”【英文标题】:Spring Boot Reactive WebClient "serverWebExchange must be null" when Spring Security OAuth is in use 【发布时间】:2019-11-20 05:39:05 【问题描述】:我想从使用 Spring Security OAuth 2 客户端凭据设置的 Spring Boot WebFlux 应用程序中使用 WebClient。
但是,我得到:java.lang.IllegalArgumentException: serverWebExchange must be null
代码在这里:https://github.com/mparaz/spring-apigee-client
当我通过从pom.xml
中删除 Spring Security 来禁用它时,它可以正常工作。
当我继续使用 Spring Security 时,不是将 webClient()
链结果返回给控制器,而是将其打印出来,它也可以工作。
使用 Spring Security 时,Reactive 客户端和服务器似乎无法一起工作。我怎样才能让它们一起运行?
【问题讨论】:
您已经使用未经身份验证的 repo/exchange 设置了 webclient。本质上,它期望对 Reactive servlet 上下文持有者进行 null 或匿名身份验证。当它拨打电话时 感谢@DarrenForsythe。您能否建议我应该在 github.com/mparaz/spring-apigee-client/blob/develop/src/main/… 中做些什么?而不是: ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository()); 应该能够自动连接ServerOAuth2AuthorizedClientRepository
,只有两个具体的 impl 可用。 UnAuthenticated 是另一个没有关联主体的请求
我遇到了完全相同的问题。当我通过单元测试调用受 oauth2 客户端凭据保护的资源时,一切都很好。但是当我通过未经身份验证的休息请求调用它时,它似乎传播了反应式 servlet 上下文持有者,而不是创建一个新的。
【参考方案1】:
似乎如果您使用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;
【讨论】:
【参考方案2】:对我来说,问题出在
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.
对以下链接的评论对我有用
https://www.gitmemory.com/issue/spring-projects/spring-security/8444/621567261
另一个链接
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
【讨论】:
ServerOAuth2AuthorizedClientExchangeFilterFunction 构造函数需要 ReactiveOAuth2AuthorizedClientManager 类型。您的“实际代码”无法编译。以上是关于使用 Spring Security OAuth 时 Spring Boot Reactive WebClient “serverWebExchange must be null”的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security/OAuth 如何找出 AuthenticationPrincipal
如何使用 redis 使用 spring-security-oauth2 持久化令牌
如何使用 XML 使用 Spring Security Oauth2 启用 /oauth/check_token
弃用 spring-security-oauth 作为客户端