Spring Security 5在Application Runner中调用OAuth2 Secured API会导致IllegalArgumentException

时间:2019-03-22 23:10:49

标签: java spring-boot spring-security spring-webflux

给出以下代码,是否可以在应用程序运行程序中调用受客户端凭据保护的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可以使用,它是在应用程序启动时调用的。

除了我自己的无操作OAuth2AuthorizedClientRepository以外,还有其他解决方法吗?

//编辑,

这不是完全反应堆。它是一个Spring Web堆栈,其中使用了WebClient。

我很清楚ServerOAuth2AuthorizedClientExchangeFilterFunction适用于完全反应堆,并且需要ReactiveClientRegistrationRepositoryReactiveOauth2AuthorizedClient,由于它们位于Servlet之上的应用程序中而无法使用堆栈,无反应。

2 个答案:

答案 0 :(得分:17)

由于我也偶然发现了这个问题,因此我将详细介绍Darren Forsythe's更新的答案,以使其他人更容易找到:

OP提交的问题导致OAuth2AuthorizedClientManager的实施能够实现

在HttpServletRequest上下文之外进行操作,例如在预定/后台线程和/或服务层中

from the official docs

上述实现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();
}

答案 1 :(得分:1)

我最终向Spring Security团队问了这个问题

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

不幸的是,如果您在servlet堆栈上并在后台线程中使用纯Spring Security 5 API调用OAuth2资源,则没有OAuth2AuthorizedClientRepository可用。

实际上有两个选择,

  1. 实施完全无操作的版本,
 var oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepo,
                        new OAuth2AuthorizedClientRepository() {
                            @Override
                            public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String s,
                                    Authentication authentication, HttpServletRequest httpServletRequest) {
                                return null;
                            }

                            @Override
                            public void saveAuthorizedClient(OAuth2AuthorizedClient oAuth2AuthorizedClient,
                                    Authentication authentication, HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse) {

                            }

                            @Override
                            public void removeAuthorizedClient(String s, Authentication authentication,
                                    HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

                            }
                        });
  1. 实现UnAuthenticatedServerOAuth2AuthorizedClientRepository的Servlet版本。 UnAuthenticatedServerOAuth2AuthorizedClientRepository GitHub Source比纯空操作具有一些基本功能。

提供有关GitHub问题的反馈可能有助于Spring Security团队评估接受PR并维护UnAuthenticatedServerOAuth2AuthorizedClientRepository的Servlet版本。

我与Spring Security Team Spring Security Issue 6683进行了联系,并在其背面提供了ServerOAuth2AuthorizedClientExchangeFilterFunction的Servlet版本,以在非HTTP线程上使用。