Spring Security 5 Replacement for OAuth2RestTemplate

谁说胖子不能爱 提交于 2020-02-26 13:06:41

问题


In spring-security-oauth2:2.4.0.RELEASE classes such as OAuth2RestTemplate, OAuth2ProtectedResourceDetails and ClientCredentialsAccessTokenProvider have all been marked as deprecated.

From the javadoc on these classes it points to a spring security migration guide that insinuates that people should migrate to the core spring-security 5 project. However I'm having trouble finding how I would implement my use case in this project.

All of the documentation and examples talk about integrating with a 3rd part OAuth provider if you want incoming requests to your application to be authenticated and you want to use the 3rd party OAuth provider to verify the identity.

In my use case all I want to do is make a request with a RestTemplate to an external service that is protected by OAuth. Currently I create an OAuth2ProtectedResourceDetails with my client id and secret which I pass into an OAuth2RestTemplate. I also have a custom ClientCredentialsAccessTokenProvider added to the OAuth2ResTemplate that just adds some extra headers to the token request that are required by the OAuth provider I'm using.

In the spring-security 5 documentation I've found a section that mentions customising the token request, but again that looks to be in the context of authenticating an incoming request with a 3rd party OAuth provider. It is not clear how you would use this in combination with something like a ClientHttpRequestInterceptor to ensure that each outgoing request to an external service first gets a token and then gets that added to the request.

Also in the migration guide linked above there is reference to a OAuth2AuthorizedClientService which it says is useful for using in interceptors, but again this looks like it relies on things like the ClientRegistrationRepository which seems to be where it maintains registrations for third party providers if you want to use that provide to ensure an incoming request is authenticated.

Is there any way I can make use of the new functionality in spring-security 5 for registering OAuth providers in order to get a token to add to outgoing requests from my application?


回答1:


OAuth 2.0 Client features of Spring Security 5.2.x do not support RestTemplate, but only WebClient. See Spring Security Reference:

HTTP Client support

  • WebClient integration for Servlet Environments (for requesting protected resources)

In addition, RestTemplate will be deprecated in a future version. See RestTemplate javadoc:

NOTE: As of 5.0, the non-blocking, reactive org.springframework.web.reactive.client.WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward. See the WebClient section of the Spring Framework reference documentation for more details and example code.

Therefore, the best solution would be to abandon RestTemplate in favor of WebClient.


Using WebClient for Client Credentials Flow

Configure client registration and provider either programmatically or using Spring Boot auto-configuration:

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

Configure the WebClient instance to use ServerOAuth2AuthorizedClientExchangeFilterFunction. It is worth noting that UnAuthenticatedServerOAuth2AuthorizedClientRepository is used, since this is a machine-to-machine communication in which the end user is not involved. Also, if you have several client registrations, you can omit a default value here, but specify it when defining a request:

@Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
    ServerOAuth2AuthorizedClientExchangeFilterFunction oauthFilter =
      new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations,
        new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
    oauth.setDefaultClientRegistrationId("custom");
    return WebClient.builder()
      .filter(oauthFilter)
      .build();
}

Now, if you try to make a request using this WebClient instance, it will first request a token from the authorization server and include it in the request.




回答2:


The above answer from @Anar Sultanov helped me get to this point, but as I had to add some additional headers to my OAuth token request I thought I would provide a full answer for how I solved the issue for my use case.

Configure provider details

Add the following to application.properties

spring.security.oauth2.client.registration.uaa.client-id=${CLIENT_ID:}
spring.security.oauth2.client.registration.uaa.client-secret=${CLIENT_SECRET:}
spring.security.oauth2.client.registration.uaa.scope=${SCOPE:}
spring.security.oauth2.client.registration.uaa.authorization-grant-type=client_credentials
spring.security.oauth2.client.provider.uaa.token-uri=${UAA_URL:}

Implement custom ReactiveOAuth2AccessTokenResponseClient

As this is server-to-server communication we need to use the ServerOAuth2AuthorizedClientExchangeFilterFunction. This only accepts a ReactiveOAuth2AuthorizedClientManager, not the non-reactive OAuth2AuthorizedClientManager. Therefore when we use ReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider() (to give it the provider to use to make the OAuth2 request) we have to give it a ReactiveOAuth2AuthorizedClientProvider instead of the non-reactive OAuth2AuthorizedClientProvider. As per the spring-security reference documentation if you use a non-reactive DefaultClientCredentialsTokenResponseClient you can use the .setRequestEntityConverter() method to alter the OAuth2 token request, but the reactive equivalent WebClientReactiveClientCredentialsTokenResponseClient does not provide this facility, so we have to implement our own (we can make use of the existing WebClientReactiveClientCredentialsTokenResponseClient logic).

My implementation was called UaaWebClientReactiveClientCredentialsTokenResponseClient (implementation omitted as it only very slightly alters the headers() and body() methods from the default WebClientReactiveClientCredentialsTokenResponseClient to add some extra headers/body fields, it does not change the underlying auth flow).

Configure WebClient

The ServerOAuth2AuthorizedClientExchangeFilterFunction.setClientCredentialsTokenResponseClient() method has been deprecated, so following the deprecation advice from that method:

Deprecated. Use ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager) instead. Create an instance of ClientCredentialsReactiveOAuth2AuthorizedClientProvider configured with a WebClientReactiveClientCredentialsTokenResponseClient (or a custom one) and than supply it to DefaultReactiveOAuth2AuthorizedClientManager.

This ends up with configuration looking something like:

@Bean("oAuth2WebClient")
public WebClient oauthFilteredWebClient(final ReactiveClientRegistrationRepository 
    clientRegistrationRepository)
{
    final ClientCredentialsReactiveOAuth2AuthorizedClientProvider
        clientCredentialsReactiveOAuth2AuthorizedClientProvider =
            new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();
    clientCredentialsReactiveOAuth2AuthorizedClientProvider.setAccessTokenResponseClient(
        new UaaWebClientReactiveClientCredentialsTokenResponseClient());

    final DefaultReactiveOAuth2AuthorizedClientManager defaultReactiveOAuth2AuthorizedClientManager =
        new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository,
            new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
    defaultReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider(
        clientCredentialsReactiveOAuth2AuthorizedClientProvider);

    final ServerOAuth2AuthorizedClientExchangeFilterFunction oAuthFilter =
        new ServerOAuth2AuthorizedClientExchangeFilterFunction(defaultReactiveOAuth2AuthorizedClientManager);
    oAuthFilter.setDefaultClientRegistrationId("uaa");

    return WebClient.builder()
        .filter(oAuthFilter)
        .build();
}

Use WebClient as normal

The oAuth2WebClient bean is now ready to be used to access resources protected by our configured OAuth2 provider in the way you would make any other request using a WebClient.



来源:https://stackoverflow.com/questions/58982286/spring-security-5-replacement-for-oauth2resttemplate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!