Dans spring-security-oauth2:2.4.0.RELEASE
classes telles que OAuth2RestTemplate
, OAuth2ProtectedResourceDetails
et ClientCredentialsAccessTokenProvider
ont tous été marqués comme obsolètes.
Du javadoc sur ces classes, il pointe vers un Spring security migration guide qui insinue que les gens doivent migrer vers le projet de base spring-security 5. Cependant, j'ai du mal à trouver comment j'implémenterais mon cas d'utilisation dans ce projet.
Toute la documentation et les exemples parlent d'intégration avec une tierce partie OAuth fournisseur si vous voulez que les requêtes entrantes vers votre application soient authentifiées et que vous souhaitez utiliser la tierce partie OAuth fournisseur pour vérifier l'identité.
Dans mon cas d'utilisation, tout ce que je veux faire est de faire une demande avec un RestTemplate
à un service externe protégé par OAuth. Actuellement, je crée un OAuth2ProtectedResourceDetails
avec mon identifiant client et mon secret que je passe dans un OAuth2RestTemplate
. J'ai également un ClientCredentialsAccessTokenProvider
personnalisé ajouté au OAuth2ResTemplate
qui ajoute simplement des en-têtes supplémentaires à la demande de jeton requis par le fournisseur OAuth que j'utilise).
Dans la documentation de spring-security 5, j'ai trouvé une section qui mentionne personnalisation de la demande de jeton , mais qui semble être dans le contexte de l'authentification d'une demande entrante avec un tiers OAuth. On ne sait pas comment utiliser cela en combinaison avec quelque chose comme un ClientHttpRequestInterceptor
pour garantir que chaque demande sortante à un service externe obtient d'abord un jeton, puis obtient celui ajouté à la demande. .
Toujours dans le guide de migration lié ci-dessus, il est fait référence à un OAuth2AuthorizedClientService
dont il dit qu'il est utile pour l'utilisation dans les intercepteurs, mais encore une fois, il semble qu'il repose sur des choses comme le ClientRegistrationRepository
qui semble être l'endroit où il maintient les enregistrements pour les fournisseurs tiers si vous souhaitez utiliser ce fournir pour garantir une demande entrante est authentifiée.
Existe-t-il un moyen d'utiliser les nouvelles fonctionnalités de Spring-Security 5 pour enregistrer les fournisseurs OAuth) afin d'obtenir un jeton à ajouter aux demandes sortantes de mon application?
Salut, il est peut-être trop tard, mais RestTemplate est toujours pris en charge dans Spring Security 5, pour une application non réactive RestTemplate est toujours utilisé ce que vous devez faire est de configurer correctement la sécurité Spring et de créer un intercepteur comme mentionné dans le guide de migration
Utilisez la configuration suivante pour utiliser le flux client_credentials
application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: ${okta.oauth2.issuer}/v1/keys
client:
registration:
okta:
client-id: ${okta.oauth2.clientId}
client-secret: ${okta.oauth2.clientSecret}
scope: "custom-scope"
authorization-grant-type: client_credentials
provider: okta
provider:
okta:
authorization-uri: ${okta.oauth2.issuer}/v1/authorize
token-uri: ${okta.oauth2.issuer}/v1/token
Configuration vers OauthResTemplate
@Configuration
@RequiredArgsConstructor
public class OAuthRestTemplateConfig {
public static final String OAUTH_WEBCLIENT = "OAUTH_WEBCLIENT";
private final RestTemplateBuilder restTemplateBuilder;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final ClientRegistrationRepository clientRegistrationRepository;
@Bean(OAUTH_WEBCLIENT)
RestTemplate oAuthRestTemplate() {
var clientRegistration = clientRegistrationRepository.findByRegistrationId(Constants.OKTA_AUTH_SERVER_ID);
return restTemplateBuilder
.additionalInterceptors(new OAuthClientCredentialsRestTemplateInterceptorConfig(authorizedClientManager(), clientRegistration))
.setReadTimeout(Duration.ofSeconds(5))
.setConnectTimeout(Duration.ofSeconds(1))
.build();
}
@Bean
OAuth2AuthorizedClientManager authorizedClientManager() {
var authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
var authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
Intercepteur
public class OAuthClientCredentialsRestTemplateInterceptor implements ClientHttpRequestInterceptor {
private final OAuth2AuthorizedClientManager manager;
private final Authentication principal;
private final ClientRegistration clientRegistration;
public OAuthClientCredentialsRestTemplateInterceptor(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
this.manager = manager;
this.clientRegistration = clientRegistration;
this.principal = createPrincipal();
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(clientRegistration.getRegistrationId())
.principal(principal)
.build();
OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest);
if (isNull(client)) {
throw new IllegalStateException("client credentials flow on " + clientRegistration.getRegistrationId() + " failed, client is null");
}
request.getHeaders().add(HttpHeaders.AUTHORIZATION, BEARER_PREFIX + client.getAccessToken().getTokenValue());
return execution.execute(request, body);
}
private Authentication createPrincipal() {
return new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return this;
}
@Override
public boolean isAuthenticated() {
return false;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return clientRegistration.getClientId();
}
};
}
}
Cela générera access_token lors du premier appel et à chaque expiration du jeton. OAuth2AuthorizedClientManager gérera tout cela pour vous
J'ai trouvé la réponse de @matt Williams très utile. Bien que j'aimerais ajouter au cas où quelqu'un voudrait transmettre par programme clientId et secret pour la configuration WebClient. Voici comment cela peut être fait.
@Configuration
public class WebClientConfig {
public static final String TEST_REGISTRATION_ID = "test-client";
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
var clientRegistration = ClientRegistration.withRegistrationId(TEST_REGISTRATION_ID)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientId("<client_id>")
.clientSecret("<client_secret>")
.tokenUri("<token_uri>")
.build();
return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
}
@Bean
public WebClient testWebClient(ReactiveClientRegistrationRepository clientRegistrationRepo) {
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepo, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId(TEST_REGISTRATION_ID);
return WebClient.builder()
.baseUrl("https://.test.com")
.filter(oauth)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
}
}