Después de de esta prolongada compilación de información dimos con la solución esta interrogante que tienen ciertos usuarios. Te dejamos la solución y nuestro deseo es servirte de mucha apoyo.
Solución:
Las características del cliente OAuth 2.0 de Spring Security 5.2.x no son compatibles RestTemplate
, pero sólo WebClient
. Consulte la referencia de seguridad de Spring:
Soporte de cliente HTTP
WebClient
integración para entornos Servlet (para solicitar recursos protegidos)
Además, RestTemplate
quedará obsoleto en una versión futura. Ver RestTemplate javadoc:
NOTA: A partir de 5.0, el reactivo no bloqueante
org.springframework.web.reactive.client.WebClient
ofrece una alternativa moderna a laRestTemplate
con soporte eficiente para sincronización y async, así como escenarios de transmisión. losRestTemplate
quedará obsoleto en una versión futura y no se agregarán nuevas funciones importantes en el futuro. Ver elWebClient
sección de la documentación de referencia de Spring Framework para obtener más detalles y código de ejemplo.
Por tanto, la mejor solución sería abandonar RestTemplate
a favor de WebClient
.
Utilizando WebClient
para el flujo de credenciales del cliente
Configure el registro del cliente y el proveedor de forma programática o mediante la configuración automática de Spring Boot:
spring:
security:
oauth2:
client:
registration:
custom:
client-id: clientId
client-secret: clientSecret
authorization-grant-type: client_credentials
provider:
custom:
token-uri: http://localhost:8081/oauth/token
… y el OAuth2AuthorizedClientManager
@Bean
:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository)
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
Configurar el WebClient
instancia para usar ServerOAuth2AuthorizedClientExchangeFilterFunction
con el proporcionado OAuth2AuthorizedClientManager
:
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager)
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("custom");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
Ahora, si intenta realizar una solicitud utilizando este WebClient
Por ejemplo, primero solicitará un token del servidor de autorización y lo incluirá en la solicitud.
Hola, tal vez sea demasiado tarde, sin embargo, RestTemplate todavía es compatible con Spring Security 5, para la aplicación no reactiva RestTemplate todavía se usa, lo que tiene que hacer es solo configurar Spring Security correctamente y crear un interceptor como se menciona en la guía de migración
Use la siguiente configuración para usar el flujo 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
Configuración de 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;
Interceptador
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();
;
Esto generará access_token en la primera llamada y siempre que caduque el token. OAuth2AuthorizedClientManager se encargará de todo esto
Encontré la respuesta de @matt Williams bastante útil. Aunque me gustaría agregar en caso de que alguien quisiera pasar programáticamente el clientId y el secreto para la configuración de WebClient. Así es como se puede hacer.
@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("")
.clientSecret("")
.tokenUri("")
.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);
Valoraciones y comentarios
Ten en cuenta dar difusión a este enunciado si lograste el éxito.