Итак, я внедряю 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? в качестве альтернативы .. есть ли другой способ получить журналы отладки уровня проводника от этого?