web-dev-qa-db-ja.com

Spring Security 5 ApplicationRunnerでOAuth2Secured 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-opOAuth2AuthorizedClientRepositoryを作成する以外の回避策はありますか?

//編集、

これは完全にリアクティブなスタックではありません。これは、WebClientが使用されているSpringWebスタックです。

完全にリアクティブなスタックに適用され、サーブレットスタック上に構築されたアプリケーションにあるために使用できないReactiveClientRegistrationRepositoryServerOAuth2AuthorizedClientExchangeFilterFunctionを必要とするReactiveOauth2AuthorizedClientをよく知っています。反応しません。

8
Darren Forsythe

私もこの問題に遭遇したので、他の人が見つけやすくするために Darren Forsythe's 更新された回答について少し詳しく説明します。

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

私はこれをSpringSecurityチームに尋ねることになりました。

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

残念ながら、サーブレットスタックを使用していて、バックグラウンドスレッドで純粋なSpring Security 5 APIを使用してOAuth2リソースを呼び出す場合、利用可能なOAuth2AuthorizedClientRepositoryはありません。

現実的には2つのオプションがあります。

  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を実装します。 nAuthenticatedServerOAuth2AuthorizedClientRepository GitHub Source これは純粋なno-opよりもいくつかの基本的な機能を備えています。

GitHubの問題に関するフィードバックを提供すると、Spring SecurityチームがPRの受け入れと、サーブレットバージョンのUnAuthenticatedServerOAuth2AuthorizedClientRepositoryの維持を評価するのに役立つ場合があります。

Spring Securityチームに連絡しました Spring Security Issue 668 そして、その後ろにServerOAuth2AuthorizedClientExchangeFilterFunctionのサーブレットバージョンがSpringSecurity 5.2に追加され、http以外のスレッドで使用できるようになります。

1
Darren Forsythe