java - Spring Security 5 在 Application Runner 中调用 OAuth2 Secured API 导致 IllegalArgumentException
问题描述
给定以下代码,是否可以在应用程序运行器中调用受客户端凭据保护的 API?
@Bean
public ApplicationRunner test(
WebClient.Builder builder,
ClientRegistrationRepository clientRegistrationRepo,
OAuth2AuthorizedClientRepository authorizedClient) {
return args -> {
try {
var oauth2 =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrationRepo,
authorizedClient);
oauth2.setDefaultClientRegistrationId("test");
var response = builder
.apply(oauth2.oauth2Configuration())
.build()
.get()
.uri("test")
.retrieve()
.bodyToMono(String.class)
.block();
log.info("Response - {}", response);
} catch (Exception e) {
log.error("Failed to call test.", e);
}
};
}
代码失败的原因是,
java.lang.IllegalArgumentException: request cannot be null
全栈,
java.lang.IllegalArgumentException: request cannot be null
at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository.loadAuthorizedClient(HttpSessionOAuth2AuthorizedClientRepository.java:47) ~[spring-security-oauth2-client-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.populateDefaultOAuth2AuthorizedClient(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:364) ~[spring-security-oauth2-client-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$null$2(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:209) ~[spring-security-oauth2-client-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.attributes(DefaultWebClient.java:234) ~[spring-webflux-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.attributes(DefaultWebClient.java:153) ~[spring-webflux-5.1.5.RELEASE.jar:5.1.5.RELEASE]
失败的方法看起来像,
public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(
String clientRegistrationId, Authentication principal, HttpServletRequest request){
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
Assert.notNull(request, "request cannot be null");
return (OAuth2AuthorizedClient)this
.getAuthorizedClients(request)
.get(clientRegistrationId);
}
这是有道理的,因为HttpServletRequest
它没有使用,它在应用程序启动时被调用。
除了让我自己的 no-op 之外,还有其他解决方法OAuth2AuthorizedClientRepository
吗?
//编辑,
这不是一个完全反应堆。它是一个 Spring Web 堆栈,其中使用了 WebClient。
我很清楚ServerOAuth2AuthorizedClientExchangeFilterFunction
哪些适用于完全反应式堆栈和要求ReactiveClientRegistrationRepository
,ReactiveOauth2AuthorizedClient
哪些不可用,因为这是在 Servlet 堆栈之上构建的应用程序中,而不是反应式。
解决方案
由于我也偶然发现了这个问题,我将详细说明Darren Forsythe 的更新答案,以便其他人更容易找到:
OP 提交的问题导致了一个OAuth2AuthorizedClientManager
能够
在 HttpServletRequest 上下文之外运行,例如在调度/后台线程和/或服务层中
(来自官方文档)
所述实现,AuthorizedClientServiceOAuth2AuthorizedClientManager
,被传递给ServletOAuth2AuthorizedClientExchangeFilterFunction
以替换默认实现。
在我的示例中,这看起来像这样:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService clientService)
{
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager)
{
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(
authorizedClientManager);
oauth2.setDefaultClientRegistrationId("keycloak");
return WebClient.builder().apply(oauth2.oauth2Configuration()).build();
}
推荐阅读
- python - 我想知道为什么范围切片在 python 中不能正常工作
- android - 如何创建从 Android Service 到 Flutter 功能的通道?
- reactjs - react-multi-carousel 初始幻灯片
- python - Python Xarray.to_dataframe() 导致操作系统错误
- php - 使用 PHP cURL for paypal 获取交易详细信息时出现安全错误
- javascript - 为什么使用 Create React App 生成的 TypeScript 项目可以找到模块而 Express 项目却不能?
- git - 递归更改 Git 子模块路径
- typescript - 如何将对象键的类型限制为命名空间常量?
- javascript - 输入隐藏字段中的敏感数据
- json - 输入时的 Angular Forms Initi 属性