我正在尝试在两个微服务之间发出GET请求(使用Keycloft身份验证(。
假设微服务A向微服务B请求一些资源。微服务B有一个GET端点,它似乎可以工作,因为当我从poster或intelliJ http_client进行请求时,我可以看到正确的响应。
在微服务A中,我正在尝试做一个请求(我确实尝试过进行阻塞和非阻塞请求(:
- 阻止请求
String response = webClient.mutate()
.baseUrl(this.serverUri)
.build().get()
.uri(uriBuilder -> uriBuilder
.path("/users/tokens/{id}")
.build(userId))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction
.clientRegistrationId("keycloak"))
.retrieve()
.bodyToMono(String.class)
.doOnError(RuntimeException::new)
.block();
- 非阻塞请求:
webClient.mutate()
.baseUrl(this.serverUri)
.build().get()
.uri(uriBuilder -> uriBuilder
.path("/users/tokens/{id}")
.build(userId))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction
.clientRegistrationId("keycloak"))
.retrieve()
.bodyToMono(String.class)
.subscribe(resp -> {
JSONObject jsonObject = new JSONObject(resp);
JSONArray jsonArray = jsonObject.getJSONArray("Tokens");
for (int i = 0; i < jsonArray.length(); i++) {
log.info("token :: " + jsonArray.get(i).toString());
}
});
这是我的WebClient
配置:
@Configuration
public class WebClientConfiguration {
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.refreshToken()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
}
我所做的一切都以这种错误告终:
2021-06-29 16:44:07.854 ERROR 390692 --- [oundedElastic-1] reactor.core.publisher.Operators : Operator called default onErrorDropped
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: servletRequest cannot be null
Caused by: java.lang.IllegalArgumentException: servletRequest cannot be null
at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.5.jar:5.3.5]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to GET https://localhost:8080/api/users/tokens/94a2d4f7-b372-4e13-aa16-7b244c099721 [DefaultWebClient]
Stack trace:
at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.5.jar:5.3.5]
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:144) ~[spring-security-oauth2-client-5.4.5.jar:5.4.5]
at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$authorizeClient$24(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:552) ~[spring-security-oauth2-client-5.4.5.jar:5.4.5]
at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:85) ~[reactor-core-3.4.4.jar:3.4.4]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227) ~[reactor-core-3.4.4.jar:3.4.4]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) [reactor-core-3.4.4.jar:3.4.4]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) [reactor-core-3.4.4.jar:3.4.4]
我是不是错过了什么?
[EDIT]
按照这里的建议更改WebClientConfiguration(Spring Security 5在Application Runner中调用OAuth2 Secured API会导致IllegalArgumentException(达到了目的:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService clientService) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.refreshToken()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
有人能解释一下原因吗?
我不确定你在什么上下文中运行这个。但错误消息表明请求不是在HttpServlet请求的上下文中启动的。如果您的请求是由调度程序或类似程序启动的,那么就没有servlet请求。
Spring文档指出,正是在这种情况下,您需要AuthorizedClientServiceOAuth2AuthorizedClientManager实现。
DefaultOAuth2AuthorizedClientManager[…]在HttpServlet请求的上下文中使用的OAuth2AAuthorizedClientManager的默认实现。(当在HttpServlet请求的上下文之外操作时,请改用AuthorizedClientServiceOAuth2AuthorizedClientManager。(
来自官方文档
AuthorizedClientServiceOAuth2AuthorizedClientManager[…]OAuth2AauthorizedClientManager的一种实现,它能够在HttpServlet请求的上下文之外操作,例如在计划/后台线程和/或服务层中。(在HttpServlet请求的上下文中操作时,请改用DefaultOAuth2AuthorizedClientManager。(
来自官方文档