Как заставить Spring Security 5 OAuth 2 использовать Apache Commons для RestTemplate - PullRequest
0 голосов
/ 22 октября 2019

Итак, я внедряю SMART на FHIR для системы, что означает предоставление client_credentials.

Вот как я настроил Spring Security OAuth

@Configuration
class OauthConfig {



    @Bean
    @Scope( BeanDefinition.SCOPE_PROTOTYPE )
    Authentication principalObjectFactory() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    @Bean
    OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientRepository authorizedClientRepository) {

        OAuth2AuthorizedClientProvider authorizedClientProvider =
            OAuth2AuthorizedClientProviderBuilder.builder()
                .clientCredentials()
                .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
            new DefaultOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }
}

Я использую перехватчикчтобы попытаться получить токен и выполнить запросы с помощью клиента HAPI Fhir.

@Configuration
class FhirConfig {

    private final FhirProperties fhir;
    private final OAuth2AuthorizedClientManager manager;

    FhirConfig( FhirProperties fhir, OAuth2AuthorizedClientManager manager ) {
        this.fhir = fhir;
        this.manager = manager;
    }

    @Bean
    HttpClientConnectionManager connectionManager() {
        return new PoolingHttpClientConnectionManager(
            5000,
            TimeUnit.MILLISECONDS
        );
    }

    @Bean
    HttpRequestInterceptor removeAcceptEncoding() {
        return ( request, ctx ) -> request.removeHeaders( "Accept-Encoding" );
    }

    private void disableGzipIfDebugging( HttpClientBuilder builder ) {
        if ( LogManager.getLogger( "org.apache.http.wire" ).isDebugEnabled() ) {
            builder.disableContentCompression().addInterceptorLast( removeAcceptEncoding() );
        }
    }

    @Bean
    CloseableHttpClient forRestTemplate() {
        HttpClientBuilder builder = HttpClients.custom()
            .setConnectionManager( connectionManager() )
            .disableCookieManagement();

        disableGzipIfDebugging( builder );
        return builder.build();
    }

    @Bean
    CloseableHttpClient forFhir(
        ObjectFactory<Authentication> authentication
    ) {

        HttpClientBuilder builder = HttpClients.custom()
            .setConnectionManager( connectionManager() )
            .disableCookieManagement();

        fhir.getOauth2().map( FhirProperties.Oauth2::getRegistration )
            .ifPresent( registration -> {
                builder.addInterceptorLast( new SmartOnFhirInterceptor( registration, authentication, manager ) );
            } );
        disableGzipIfDebugging( builder );
        return builder.build();
    }


    @Bean
    public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory( ) {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setHttpClient(forRestTemplate());
        return clientHttpRequestFactory;
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplateBuilder()
            .requestFactory( this::clientHttpRequestFactory )
            .build();
    }

    @Bean
    @ConditionalOnProperty( name = "fhir.endpoint" )
    IGenericClient genericClient( CloseableHttpClient forFhir ) {
        IRestfulClientFactory rcf = new FhirContext( fhir.getVersion() ).getRestfulClientFactory();
        rcf.setHttpClient( forFhir );
        return rcf.newGenericClient( fhir.getEndpoint() );
    }

    static class SmartOnFhirInterceptor implements HttpRequestInterceptor {

        private final String registration;
        private final ObjectFactory<Authentication> authentication;
        private final OAuth2AuthorizedClientManager manager;
        private final AtomicBoolean isFirstRequest = new AtomicBoolean( true );

        SmartOnFhirInterceptor( String registration, ObjectFactory<Authentication> authentication, OAuth2AuthorizedClientManager manager ) {
            this.registration = Objects.requireNonNull( registration );
            this.authentication = Objects.requireNonNull( authentication );
            this.manager = Objects.requireNonNull( manager );
        }

        @Override
        public void process( HttpRequest req, HttpContext ctx ) {
            if ( ! isFirstRequest.getAndSet( false )) {
                OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId( registration )
                    .principal( authentication.getObject() )
                    .build();
                OAuth2AuthorizedClient oac = Objects.requireNonNull( this.manager.authorize( authorizeRequest ) );
                String token = oac.getAccessToken().getTokenValue();

                req.addHeader( "Authorization", "Bearer " + token );
            }
        }

    }
}

У меня установлено logging.level.org.apache.http.wire=debug, и я вижу журналы для первого запроса к серверу метаданных, которые должны быть не аутентифицированы(и это пропускает это). Второй запрос терпит неудачу с этим, и я не вижу журналы отладки.

Caused by: org.springframework.http.converter.HttpMessageNotReadableException: An error occurred reading the OAuth 2.0 Error: Could not read JSON: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $; nested exception is com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $; nested exception is com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
    at org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter.readInternal(OAuth2ErrorHttpMessageConverter.java:78)
    at org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter.readInternal(OAuth2ErrorHttpMessageConverter.java:46)
    at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:199)
    at org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler.handleError(OAuth2ErrorResponseErrorHandler.java:59)
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:785)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:644)
    at org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient.getTokenResponse(DefaultClientCredentialsTokenResponseClient.java:75)
    at org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient.getTokenResponse(DefaultClientCredentialsTokenResponseClient.java:52)
    at org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider.authorize(ClientCredentialsOAuth2AuthorizedClientProvider.java:83)
    at org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider.authorize(DelegatingOAuth2AuthorizedClientProvider.java:67)
    at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:107)
    at com.potreromed.phg.emr.fhir.FhirConfig$SmartOnFhirInterceptor.process(FhirConfig.java:130)
    at org.apache.http.protocol.ImmutableHttpProcessor.process(ImmutableHttpProcessor.java:133)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    at ca.uhn.fhir.rest.client.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:64)
    at ca.uhn.fhir.rest.client.impl.BaseClient.invokeClient(BaseClient.java:304)
    at ca.uhn.fhir.rest.client.impl.GenericClient$BaseClientExecutable.invoke(GenericClient.java:431)
    at ca.uhn.fhir.rest.client.impl.GenericClient$SearchInternal.execute(GenericClient.java:1830)
    at com.potreromed.phg.emr.fhir.OAuthTest.lambda$client$0(OAuthTest.java:40)
    at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:55)
    ... 91 more
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $; nested exception is com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
    at org.springframework.http.converter.json.AbstractJsonHttpMessageConverter.readResolved(AbstractJsonHttpMessageConverter.java:112)
    at org.springframework.http.converter.json.AbstractJsonHttpMessageConverter.read(AbstractJsonHttpMessageConverter.java:94)
    at org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter.readInternal(OAuth2ErrorHttpMessageConverter.java:73)
    ... 118 more
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
    at com.google.gson.Gson.fromJson(Gson.java:944)
    at com.google.gson.Gson.fromJson(Gson.java:897)
    at org.springframework.http.converter.json.GsonHttpMessageConverter.readInternal(GsonHttpMessageConverter.java:92)
    at org.springframework.http.converter.json.AbstractJsonHttpMessageConverter.readResolved(AbstractJsonHttpMessageConverter.java:109)
    ... 120 more
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
    at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:386)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:183)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
    at com.google.gson.Gson.fromJson(Gson.java:932)
    ... 123 more

Это сообщение не особенно полезно, но я чувствую, что Spring 5 не использует мой экземпляр RestTemplate, используяhttp общие. Я нашел более старый блог, вероятно, для 2.x старой библиотеки, для OAuth2RestTemplate, но этот класс, кажется, пропал.

Как я могу настроить Spring Security 5.2 Oauth2 для использования моего RestTemplate, чтобы я мог получитьжурналы отладки проводов apache? в качестве альтернативы .. есть ли другой способ получить журналы отладки уровня проводника от этого?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...