Refactor OAuth2 configuration to use Spring Security 5.2.
This commit is contained in:
@@ -34,7 +34,7 @@ ext {
|
||||
springBootVersion = "2.2.1.RELEASE"
|
||||
springSecurityVersion = "5.2.1.RELEASE"
|
||||
springCloudConnectorsVersion = "1.2.5.RELEASE"
|
||||
reactorVersion = "Dysprosium-SR1"
|
||||
reactorVersion = "Dysprosium-SR2"
|
||||
|
||||
okHttp3Version = "4.2.2"
|
||||
httpClientVersion = "4.5.10"
|
||||
|
||||
@@ -23,9 +23,9 @@ import org.springframework.credhub.core.ReactiveCredHubTemplate;
|
||||
import org.springframework.credhub.support.ClientOptions;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
|
||||
|
||||
/**
|
||||
@@ -55,15 +55,15 @@ public class CredHubTemplateFactory {
|
||||
* @param credHubProperties connection properties
|
||||
* @param clientOptions connection options
|
||||
* @param clientRegistrationRepository a repository of OAuth2 client registrations
|
||||
* @param authorizedClientService a repository of authorized OAuth2 clients
|
||||
* @param authorizedClientRepository a repository of authorized OAuth2 clients
|
||||
* @return a {@code CredHubTemplate}
|
||||
*/
|
||||
public CredHubTemplate credHubTemplate(CredHubProperties credHubProperties,
|
||||
ClientOptions clientOptions,
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientService authorizedClientService) {
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
return new CredHubTemplate(credHubProperties, clientHttpRequestFactory(clientOptions),
|
||||
clientRegistrationRepository, authorizedClientService);
|
||||
clientRegistrationRepository, authorizedClientRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,20 +23,12 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.support.HttpRequestWrapper;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
@@ -46,20 +38,12 @@ import java.util.Collections;
|
||||
*/
|
||||
class CredHubOAuth2RequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
private final ClientRegistration clientRegistration;
|
||||
private final OAuth2AuthorizedClientService authorizedClientService;
|
||||
private final OAuth2AuthorizedClientManager clientManager;
|
||||
|
||||
private final DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient;
|
||||
|
||||
private final Clock clock = Clock.systemUTC();
|
||||
private final Duration accessTokenExpiresSkew = Duration.ofMinutes(1);
|
||||
|
||||
CredHubOAuth2RequestInterceptor(RestOperations tokenServerRestTemplate,
|
||||
ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizedClientService authorizedClientService) {
|
||||
CredHubOAuth2RequestInterceptor(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizedClientManager clientManager) {
|
||||
this.clientRegistration = clientRegistration;
|
||||
this.authorizedClientService = authorizedClientService;
|
||||
|
||||
this.clientCredentialsTokenResponseClient = createClientCredentialsTokenResponseClient(tokenServerRestTemplate);
|
||||
this.clientManager = clientManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,78 +57,30 @@ class CredHubOAuth2RequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
|
||||
|
||||
HttpHeaders headers = requestWrapper.getHeaders();
|
||||
headers.setBearerAuth(getAccessToken().getTokenValue());
|
||||
headers.setBearerAuth(authorizeClient().getAccessToken().getTokenValue());
|
||||
|
||||
return execution.execute(requestWrapper, body);
|
||||
}
|
||||
|
||||
private OAuth2AccessToken getAccessToken() {
|
||||
OAuth2AuthorizedClient client = authorizedClientService
|
||||
.loadAuthorizedClient(clientRegistration.getRegistrationId(), clientRegistration.getClientId());
|
||||
|
||||
if (client == null || tokenExpiring(client)) {
|
||||
client = authorizeClient();
|
||||
}
|
||||
|
||||
return client.getAccessToken();
|
||||
}
|
||||
|
||||
private OAuth2AuthorizedClient authorizeClient() {
|
||||
OAuth2ClientCredentialsGrantRequest request =
|
||||
new OAuth2ClientCredentialsGrantRequest(clientRegistration);
|
||||
OAuth2AccessTokenResponse tokenResponse = clientCredentialsTokenResponseClient.getTokenResponse(request);
|
||||
|
||||
OAuth2AccessToken accessToken = tokenResponse.getAccessToken();
|
||||
OAuth2RefreshToken refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
OAuth2AuthorizedClient authorizedClient =
|
||||
new OAuth2AuthorizedClient(clientRegistration,
|
||||
clientRegistration.getClientId(),
|
||||
accessToken, refreshToken);
|
||||
|
||||
saveAuthorizedClient(clientRegistration, accessToken, authorizedClient);
|
||||
|
||||
return authorizedClient;
|
||||
}
|
||||
|
||||
private boolean tokenExpiring(OAuth2AuthorizedClient client) {
|
||||
Instant now = this.clock.instant();
|
||||
Instant expiresAt = client.getAccessToken().getExpiresAt();
|
||||
if (expiresAt != null && now.isAfter(expiresAt.minus(this.accessTokenExpiresSkew))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void saveAuthorizedClient(ClientRegistration clientRegistration,
|
||||
OAuth2AccessToken accessToken,
|
||||
OAuth2AuthorizedClient authorizedClient) {
|
||||
OAuth2ClientCredentialsGrantAuthenticationToken authentication =
|
||||
new OAuth2ClientCredentialsGrantAuthenticationToken(clientRegistration, accessToken);
|
||||
authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);
|
||||
}
|
||||
|
||||
private static DefaultClientCredentialsTokenResponseClient createClientCredentialsTokenResponseClient(RestOperations restTemplate) {
|
||||
DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
|
||||
new DefaultClientCredentialsTokenResponseClient();
|
||||
clientCredentialsTokenResponseClient.setRestOperations(restTemplate);
|
||||
return clientCredentialsTokenResponseClient;
|
||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
|
||||
.withClientRegistrationId(clientRegistration.getRegistrationId())
|
||||
.principal(new OAuth2ClientCredentialsGrantAuthenticationToken(clientRegistration))
|
||||
.build();
|
||||
return clientManager.authorize(authorizeRequest);
|
||||
}
|
||||
|
||||
private static class OAuth2ClientCredentialsGrantAuthenticationToken extends AbstractAuthenticationToken {
|
||||
private final ClientRegistration clientRegistration;
|
||||
private final OAuth2AccessToken accessToken;
|
||||
|
||||
OAuth2ClientCredentialsGrantAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AccessToken accessToken) {
|
||||
OAuth2ClientCredentialsGrantAuthenticationToken(ClientRegistration clientRegistration) {
|
||||
super(Collections.emptyList());
|
||||
this.clientRegistration = clientRegistration;
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return accessToken.getTokenValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,12 +34,18 @@ import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
|
||||
import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
|
||||
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
|
||||
@@ -76,20 +82,21 @@ class CredHubRestTemplateFactory {
|
||||
* @param clientHttpRequestFactory the {@link ClientHttpRequestFactory} to use when
|
||||
* creating new connections
|
||||
* @param clientRegistrationRepository a repository of OAuth2 client registrations
|
||||
* @param authorizedClientService a repository of authorized OAuth2 clients
|
||||
* @param authorizedClientRepository a repository of authorized OAuth2 clients
|
||||
* @return a configured {@link RestTemplate}
|
||||
*/
|
||||
static RestTemplate createRestTemplate(CredHubProperties properties,
|
||||
ClientHttpRequestFactory clientHttpRequestFactory,
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientService authorizedClientService) {
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
configureRestTemplate(restTemplate, properties.getUrl(), clientHttpRequestFactory);
|
||||
configureOAuth2(restTemplate, clientHttpRequestFactory,
|
||||
configureOAuth2(restTemplate,
|
||||
clientHttpRequestFactory,
|
||||
properties.getOauth2().getRegistrationId(),
|
||||
clientRegistrationRepository,
|
||||
authorizedClientService);
|
||||
authorizedClientRepository);
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
@@ -121,13 +128,13 @@ class CredHubRestTemplateFactory {
|
||||
* creating new connections
|
||||
* @param clientId the OAuth2 client ID for authentication
|
||||
* @param clientRegistrationRepository a repository of OAuth2 client registrations
|
||||
* @param authorizedClientService a repository of authorized OAuth2 clients
|
||||
* @param authorizedClientRepository a repository of authorized OAuth2 clients
|
||||
*/
|
||||
private static void configureOAuth2(RestTemplate restTemplate,
|
||||
ClientHttpRequestFactory clientHttpRequestFactory,
|
||||
String clientId,
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientService authorizedClientService) {
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId(clientId);
|
||||
|
||||
if (clientRegistration == null) {
|
||||
@@ -135,11 +142,39 @@ class CredHubRestTemplateFactory {
|
||||
"' is not a valid Spring Security OAuth2 client registration");
|
||||
}
|
||||
|
||||
RestOperations tokenServerRestTemplate = createTokenServerRestTemplate(clientHttpRequestFactory);
|
||||
OAuth2AuthorizedClientManager clientManager =
|
||||
buildClientManager(clientRegistrationRepository, authorizedClientRepository, clientHttpRequestFactory);
|
||||
|
||||
restTemplate.getInterceptors()
|
||||
.add(new CredHubOAuth2RequestInterceptor(tokenServerRestTemplate,
|
||||
clientRegistration, authorizedClientService));
|
||||
.add(new CredHubOAuth2RequestInterceptor(clientRegistration, clientManager));
|
||||
}
|
||||
|
||||
private static OAuth2AuthorizedClientManager buildClientManager(
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository,
|
||||
ClientHttpRequestFactory clientHttpRequestFactory) {
|
||||
|
||||
OAuth2AuthorizedClientProvider authorizedClientProvider =
|
||||
OAuth2AuthorizedClientProviderBuilder.builder()
|
||||
.authorizationCode()
|
||||
.clientCredentials(b ->
|
||||
b.accessTokenResponseClient(buildTokenResponseClient(clientHttpRequestFactory)))
|
||||
.build();
|
||||
|
||||
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
|
||||
new DefaultOAuth2AuthorizedClientManager(
|
||||
clientRegistrationRepository, authorizedClientRepository);
|
||||
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
|
||||
return authorizedClientManager;
|
||||
}
|
||||
|
||||
private static OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> buildTokenResponseClient(
|
||||
ClientHttpRequestFactory clientHttpRequestFactory) {
|
||||
DefaultClientCredentialsTokenResponseClient tokenResponseClient =
|
||||
new DefaultClientCredentialsTokenResponseClient();
|
||||
tokenResponseClient.setRestOperations(createTokenServerRestTemplate(clientHttpRequestFactory));
|
||||
return tokenResponseClient;
|
||||
}
|
||||
|
||||
private static RestTemplate createTokenServerRestTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
|
||||
|
||||
@@ -29,8 +29,8 @@ import org.springframework.credhub.core.permission.CredHubPermissionTemplate;
|
||||
import org.springframework.credhub.core.permissionV2.CredHubPermissionV2Operations;
|
||||
import org.springframework.credhub.core.permissionV2.CredHubPermissionV2Template;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -82,18 +82,18 @@ public class CredHubTemplate implements CredHubOperations {
|
||||
* @param clientHttpRequestFactory the {@link ClientHttpRequestFactory} to use when
|
||||
* creating new connections
|
||||
* @param clientRegistrationRepository a repository of OAuth2 client registrations
|
||||
* @param authorizedClientService a repository of authorized OAuth2 clients
|
||||
* @param authorizedClientRepository a repository of authorized OAuth2 clients
|
||||
*/
|
||||
public CredHubTemplate(CredHubProperties properties,
|
||||
ClientHttpRequestFactory clientHttpRequestFactory,
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientService authorizedClientService) {
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
Assert.notNull(properties, "properties must not be null");
|
||||
Assert.notNull(clientHttpRequestFactory, "clientHttpRequestFactory must not be null");
|
||||
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository must not be null");
|
||||
|
||||
this.restTemplate = CredHubRestTemplateFactory.createRestTemplate(properties,
|
||||
clientHttpRequestFactory, clientRegistrationRepository, authorizedClientService);
|
||||
clientHttpRequestFactory, clientRegistrationRepository, authorizedClientRepository);
|
||||
this.usingOAuth2 = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.credhub.core;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.credhub.security.oauth2.client.endpoint.WebClientReactiveClientCredentialsTokenResponseClient;
|
||||
import org.springframework.credhub.support.utils.JsonUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -25,7 +24,11 @@ import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.codec.CodecConfigurer;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
|
||||
import org.springframework.security.oauth2.client.endpoint.WebClientReactiveClientCredentialsTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
@@ -56,26 +59,24 @@ class CredHubWebClientFactory {
|
||||
/**
|
||||
* Create a {@link WebClient} configured for communication with a CredHub server.
|
||||
*
|
||||
* @param properties CredHub connection properties
|
||||
* @param clientHttpConnector the {@link ClientHttpConnector} to use when
|
||||
* creating new connections
|
||||
* @param properties CredHub connection properties
|
||||
* @param clientHttpConnector the {@link ClientHttpConnector} to use when
|
||||
* creating new connections
|
||||
* @param clientRegistrationRepository a repository of OAuth2 client registrations
|
||||
* @param authorizedClientRepository a repository of OAuth2 authorized clients
|
||||
* @param authorizedClientRepository a repository of OAuth2 authorized clients
|
||||
* @return a configured {@link WebClient}
|
||||
*/
|
||||
static WebClient createWebClient(CredHubProperties properties, ClientHttpConnector clientHttpConnector,
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
||||
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
WebClientReactiveClientCredentialsTokenResponseClient tokenResponseClient =
|
||||
new WebClientReactiveClientCredentialsTokenResponseClient();
|
||||
tokenResponseClient.setWebClient(WebClient.builder()
|
||||
.clientConnector(clientHttpConnector)
|
||||
.build());
|
||||
ReactiveOAuth2AuthorizedClientProvider clientProvider =
|
||||
buildClientProvider(clientHttpConnector);
|
||||
|
||||
DefaultReactiveOAuth2AuthorizedClientManager clientManager =
|
||||
buildClientManager(clientRegistrationRepository, authorizedClientRepository, clientProvider);
|
||||
|
||||
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
|
||||
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
|
||||
authorizedClientRepository);
|
||||
oauth.setClientCredentialsTokenResponseClient(tokenResponseClient);
|
||||
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientManager);
|
||||
|
||||
return buildWebClient(properties.getUrl(), clientHttpConnector)
|
||||
.filter(oauth)
|
||||
@@ -86,6 +87,36 @@ class CredHubWebClientFactory {
|
||||
.build();
|
||||
}
|
||||
|
||||
private static ReactiveOAuth2AuthorizedClientProvider buildClientProvider(
|
||||
ClientHttpConnector clientHttpConnector) {
|
||||
return ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
|
||||
.authorizationCode()
|
||||
.clientCredentials(b ->
|
||||
b.accessTokenResponseClient(buildTokenResponseClient(clientHttpConnector)))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static WebClientReactiveClientCredentialsTokenResponseClient buildTokenResponseClient(
|
||||
ClientHttpConnector clientHttpConnector) {
|
||||
WebClientReactiveClientCredentialsTokenResponseClient tokenResponseClient =
|
||||
new WebClientReactiveClientCredentialsTokenResponseClient();
|
||||
tokenResponseClient.setWebClient(WebClient.builder()
|
||||
.clientConnector(clientHttpConnector)
|
||||
.build());
|
||||
return tokenResponseClient;
|
||||
}
|
||||
|
||||
private static DefaultReactiveOAuth2AuthorizedClientManager buildClientManager(
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
||||
ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
|
||||
ReactiveOAuth2AuthorizedClientProvider clientProvider) {
|
||||
DefaultReactiveOAuth2AuthorizedClientManager clientManager =
|
||||
new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository,
|
||||
authorizedClientRepository);
|
||||
clientManager.setAuthorizedClientProvider(clientProvider);
|
||||
return clientManager;
|
||||
}
|
||||
|
||||
private static WebClient.Builder buildWebClient(String baseUri, ClientHttpConnector clientHttpConnector) {
|
||||
ExchangeStrategies strategies = ExchangeStrategies.builder()
|
||||
.codecs(configurer -> {
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package org.springframework.credhub.security.oauth2.client.endpoint;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
|
||||
import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.springframework.security.oauth2.core.web.reactive.function.OAuth2BodyExtractors.oauth2AccessTokenResponse;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link ReactiveOAuth2AccessTokenResponseClient} that "exchanges"
|
||||
* an authorization code credential for an access token credential
|
||||
* at the Authorization Server's Token Endpoint.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.1
|
||||
* @see OAuth2AccessTokenResponseClient
|
||||
* @see OAuth2AuthorizationCodeGrantRequest
|
||||
* @see OAuth2AccessTokenResponse
|
||||
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-oauth-openid-connect-sdk">Nimbus OAuth 2.0 SDK</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request (Authorization Code Grant)</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.4">Section 4.1.4 Access Token Response (Authorization Code Grant)</a>
|
||||
*/
|
||||
public class WebClientReactiveClientCredentialsTokenResponseClient implements ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
|
||||
private WebClient webClient = WebClient.builder()
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2ClientCredentialsGrantRequest authorizationGrantRequest) {
|
||||
return Mono.defer(() -> {
|
||||
ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();
|
||||
|
||||
String tokenUri = clientRegistration.getProviderDetails().getTokenUri();
|
||||
BodyInserters.FormInserter<String> body = body(authorizationGrantRequest);
|
||||
|
||||
return this.webClient.post()
|
||||
.uri(tokenUri)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.headers(headers(clientRegistration))
|
||||
.body(body)
|
||||
.exchange()
|
||||
.flatMap(response -> response.body(oauth2AccessTokenResponse()))
|
||||
.map(response -> {
|
||||
if (response.getAccessToken().getScopes().isEmpty()) {
|
||||
response = OAuth2AccessTokenResponse.withResponse(response)
|
||||
.scopes(authorizationGrantRequest.getClientRegistration().getScopes())
|
||||
.build();
|
||||
}
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Consumer<HttpHeaders> headers(ClientRegistration clientRegistration) {
|
||||
return headers -> {
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
|
||||
if (ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {
|
||||
headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static BodyInserters.FormInserter<String> body(OAuth2ClientCredentialsGrantRequest authorizationGrantRequest) {
|
||||
ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();
|
||||
BodyInserters.FormInserter<String> body = BodyInserters
|
||||
.fromFormData(OAuth2ParameterNames.GRANT_TYPE, authorizationGrantRequest.getGrantType().getValue());
|
||||
Set<String> scopes = clientRegistration.getScopes();
|
||||
if (!CollectionUtils.isEmpty(scopes)) {
|
||||
String scope = StringUtils.collectionToDelimitedString(scopes, " ");
|
||||
body.with(OAuth2ParameterNames.SCOPE, scope);
|
||||
}
|
||||
if (ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) {
|
||||
body.with(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
|
||||
body.with(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setWebClient(WebClient webClient) {
|
||||
this.webClient = webClient;
|
||||
}
|
||||
}
|
||||
@@ -38,12 +38,12 @@ To use OAuth2 authentication to CredHub, add the following {spring-security}[Spr
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>5.1.0.RELEASE</version>
|
||||
<version>5.2.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-client</artifactId>
|
||||
<version>5.1.0.RELEASE</version>
|
||||
<version>5.2.1.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -58,13 +58,13 @@ Add the Spring CredHub starter to the `dependencies` section of the build file:
|
||||
To enable reactive support in Spring CredHub, add the following {spring-webflux}[Spring WebFlux] dependency to the build file:
|
||||
|
||||
dependencies {
|
||||
compile("org.springframework.boot:spring-boot-starter-webflux:5.1.0.RELEASE")
|
||||
compile("org.springframework.boot:spring-boot-starter-webflux:5.2.1.RELEASE")
|
||||
}
|
||||
|
||||
To use OAuth2 authentication to CredHub, add the following {spring-security}[Spring Security] dependencies to the build file:
|
||||
|
||||
dependencies {
|
||||
compile("org.springframework.security:spring-security-config:5.1.0.RELEASE")
|
||||
compile("org.springframework.security:spring-security-oauth2-client:5.1.0.RELEASE")
|
||||
compile("org.springframework.security:spring-security-config:5.2.1.RELEASE")
|
||||
compile("org.springframework.security:spring-security-oauth2-client:5.2.1.RELEASE")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
spring:
|
||||
credhub:
|
||||
url: ${CREDHUB_SERVER}
|
||||
ca-cert-files:
|
||||
- ${CREDHUB_CA_CERT}
|
||||
oauth2:
|
||||
registration-id: credhub-test
|
||||
security:
|
||||
oauth2:
|
||||
client:
|
||||
registration:
|
||||
credhub-test:
|
||||
provider: uaa
|
||||
client-id: ${CREDHUB_CLIENT}
|
||||
client-secret: ${CREDHUB_SECRET}
|
||||
authorization-grant-type: client_credentials
|
||||
provider:
|
||||
uaa:
|
||||
token-uri: ${UAA_SERVER}/oauth/token
|
||||
|
||||
debug: true
|
||||
logging.level.org.springframework.web: DEBUG
|
||||
@@ -0,0 +1,22 @@
|
||||
spring:
|
||||
credhub:
|
||||
url: ${CREDHUB_SERVER}
|
||||
ca-cert-files:
|
||||
- ${CREDHUB_CA_CERT}
|
||||
oauth2:
|
||||
registration-id: credhub-test
|
||||
security:
|
||||
oauth2:
|
||||
client:
|
||||
registration:
|
||||
credhub-test:
|
||||
provider: uaa
|
||||
client-id: ${CREDHUB_CLIENT}
|
||||
client-secret: ${CREDHUB_SECRET}
|
||||
authorization-grant-type: client_credentials
|
||||
provider:
|
||||
uaa:
|
||||
token-uri: ${UAA_SERVER}/oauth/token
|
||||
|
||||
debug: true
|
||||
logging.level.org.springframework.web: DEBUG
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.credhub.autoconfig;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -24,17 +24,19 @@ import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConf
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.UnAuthenticatedServerOAuth2AuthorizedClientRepository;
|
||||
|
||||
@@ -48,7 +50,7 @@ import java.util.List;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(OAuth2ClientProperties.class)
|
||||
@AutoConfigureBefore(ReactiveOAuth2ClientAutoConfiguration.class)
|
||||
@AutoConfigureAfter({OAuth2ClientAutoConfiguration.class, ReactiveOAuth2ClientAutoConfiguration.class})
|
||||
@ConditionalOnClass(name = "org.springframework.security.oauth2.client.registration.ClientRegistration")
|
||||
@Conditional(ClientsConfiguredCondition.class)
|
||||
public class CredHubOAuth2AutoConfiguration {
|
||||
@@ -67,6 +69,7 @@ public class CredHubOAuth2AutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnClass(name = "javax.servlet.http.HttpServletRequest")
|
||||
public ClientRegistrationRepository credHubClientRegistrationRepository() {
|
||||
List<ClientRegistration> registrations = new ArrayList<>(
|
||||
OAuth2ClientPropertiesRegistrationAdapter
|
||||
@@ -75,17 +78,19 @@ public class CredHubOAuth2AutoConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code OAuth2AuthorizedClientService} bean for use with an OAuth2-enabled
|
||||
* Create an {@code OAuth2AuthorizedClientRepository} bean for use with an OAuth2-enabled
|
||||
* {@code CredHubTemplate}.
|
||||
*
|
||||
* @param clientRegistrationRepository a {@code ClientRegistrationRepository}
|
||||
* @return the {@code OAuth2AuthorizedClientService}
|
||||
* @return the {@code OAuth2AuthorizedClientRepository}
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public OAuth2AuthorizedClientService credHubAuthorizedClientService(
|
||||
@ConditionalOnClass(name = "javax.servlet.http.HttpServletRequest")
|
||||
public OAuth2AuthorizedClientRepository credHubAuthorizedClientRepository(
|
||||
ClientRegistrationRepository clientRegistrationRepository) {
|
||||
return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
|
||||
return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(
|
||||
new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +120,7 @@ public class CredHubOAuth2AutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
|
||||
public ServerOAuth2AuthorizedClientRepository credHubAuthorizedClientRepository() {
|
||||
public ServerOAuth2AuthorizedClientRepository credHubReactiveAuthorizedClientRepository() {
|
||||
return new UnAuthenticatedServerOAuth2AuthorizedClientRepository();
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,9 @@ import org.springframework.credhub.core.CredHubTemplate;
|
||||
import org.springframework.credhub.core.ReactiveCredHubOperations;
|
||||
import org.springframework.credhub.core.ReactiveCredHubTemplate;
|
||||
import org.springframework.credhub.support.ClientOptions;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
|
||||
|
||||
/**
|
||||
@@ -60,26 +60,27 @@ public class CredHubTemplateAutoConfiguration {
|
||||
* @param credHubProperties {@link CredHubProperties} for CredHub
|
||||
* @param clientOptions client connection options
|
||||
* @param clientRegistrationRepository a repository of OAuth2 client registrations
|
||||
* @param authorizedClientService a repository of authorized OAuth2 clients
|
||||
* @param authorizedClientRepository a repository of authorized OAuth2 clients
|
||||
* @return the {@link CredHubOperations} bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnClass(name = "javax.servlet.http.HttpServletRequest")
|
||||
public CredHubOperations credHubTemplate(
|
||||
CredHubProperties credHubProperties, ClientOptions clientOptions,
|
||||
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository,
|
||||
@Autowired(required = false) OAuth2AuthorizedClientService authorizedClientService) {
|
||||
@Autowired(required = false) OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
|
||||
if (credHubProperties.getOauth2() == null || credHubProperties.getOauth2().getRegistrationId() == null) {
|
||||
return credHubTemplateFactory.credHubTemplate(credHubProperties, clientOptions);
|
||||
}
|
||||
|
||||
if (clientRegistrationRepository == null || authorizedClientService == null) {
|
||||
if (clientRegistrationRepository == null || authorizedClientRepository == null) {
|
||||
throw misconfiguredException();
|
||||
}
|
||||
|
||||
return credHubTemplateFactory.credHubTemplate(credHubProperties, clientOptions,
|
||||
clientRegistrationRepository, authorizedClientService);
|
||||
clientRegistrationRepository, authorizedClientRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -95,6 +95,18 @@ public class CredHubOAuth2AutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oauth2ContextConfiguredWithReactiveWebAppNoServlet() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
.withClassLoader(new FilteredClassLoader("javax.servlet"))
|
||||
.withConfiguration(AutoConfigurations.of(configurations))
|
||||
.withPropertyValues(oAuth2ClientProperties)
|
||||
.run(context -> {
|
||||
assertServletOAuth2ContextNotConfigured(context);
|
||||
assertReactiveOAuth2ContextConfigured(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oauth2ContextNotConfiguredWithoutProperties() {
|
||||
context
|
||||
@@ -112,28 +124,28 @@ public class CredHubOAuth2AutoConfigurationTests {
|
||||
ApplicationContextAssertProvider<? extends ApplicationContext> context) {
|
||||
|
||||
assertThat(context).hasBean("credHubClientRegistrationRepository");
|
||||
assertThat(context).hasBean("credHubAuthorizedClientService");
|
||||
assertThat(context).hasBean("credHubAuthorizedClientRepository");
|
||||
}
|
||||
|
||||
private void assertServletOAuth2ContextNotConfigured(
|
||||
ApplicationContextAssertProvider<? extends ApplicationContext> context) {
|
||||
|
||||
assertThat(context).doesNotHaveBean("credHubClientRegistrationRepository");
|
||||
assertThat(context).doesNotHaveBean("credHubAuthorizedClientService");
|
||||
assertThat(context).doesNotHaveBean("credHubAuthorizedClientRepository");
|
||||
}
|
||||
|
||||
private void assertReactiveOAuth2ContextConfigured(
|
||||
ApplicationContextAssertProvider<? extends ApplicationContext> context) {
|
||||
|
||||
assertThat(context).hasBean("credHubReactiveClientRegistrationRepository");
|
||||
assertThat(context).hasBean("credHubAuthorizedClientRepository");
|
||||
assertThat(context).hasBean("credHubReactiveAuthorizedClientRepository");
|
||||
}
|
||||
|
||||
private void assertReactiveOAuth2ContextNotConfigured(
|
||||
ApplicationContextAssertProvider<? extends ApplicationContext> context) {
|
||||
|
||||
assertThat(context).doesNotHaveBean("credHubReactiveClientRegistrationRepository");
|
||||
assertThat(context).doesNotHaveBean("credHubAuthorizedClientRepository");
|
||||
assertThat(context).doesNotHaveBean("credHubReactiveAuthorizedClientRepository");
|
||||
}
|
||||
|
||||
private void assertOAuth2ContextNotConfigured(
|
||||
|
||||
Reference in New Issue
Block a user