`servletRequest 不能为空` -> SpringBoot - WebClient - GET 请求与 Keycloak

问题描述

我正在尝试在两个微服务之间发出 GET 请求(使用 Keycloak 身份验证)。

假设微服务 A 向微服务 B 请求一些资源。 微服务 B 有一个 GET 端点,它似乎可以工作,因为在从邮递员或 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]

我是不是遗漏了什么?

[编辑]

按照此处的建议 (Spring Security 5 Calling OAuth2 Secured API in Application Runner results in IllegalArgumentException) 更改 WebClientConfiguration 可以解决问题:

    @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();
    }

有人能解释一下原因吗?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)