OAuth2RestTemplate için Spring Security 5 Değişimi


14

In spring-security-oauth2:2.4.0.RELEASEgibi sınıflara OAuth2RestTemplate, OAuth2ProtectedResourceDetailsve ClientCredentialsAccessTokenProviderkullanımdan kaldırıldı gibi tüm işaretlendi.

Bu sınıflardaki javadoc'tan, insanların temel bahar güvenliği 5 projesine göç etmesi gerektiğini ima eden bir bahar güvenliği göç kılavuzuna işaret ediyor. Ancak bu projede kullanım durumumu nasıl uygulayacağımı bulmakta zorlanıyorum.

Uygulamanıza gelen isteklerin kimliğini doğrulamak istiyorsanız ve kimliği doğrulamak için 3. taraf OAuth sağlayıcısını kullanmak istiyorsanız, tüm belgeler ve örnekler 3. bölüm OAuth sağlayıcısıyla tümleştirme hakkında konuşur.

Kullanım durumumda tek yapmak istediğim, RestTemplateOAuth tarafından korunan harici bir hizmete bir istekte bulunmak . Şu anda OAuth2ProtectedResourceDetailsmüşteri kimliği ve sır ile bir OAuth2RestTemplate. Ayrıca, kullandığım OAuth sağlayıcısı tarafından gerekli olan token isteğine bazı ekstra üstbilgiler ekleyen özel bir ClientCredentialsAccessTokenProviderek var OAuth2ResTemplate.

Yay güvenliği 5 dokümantasyonunda , jeton isteğini özelleştirmekten bahseden bir bölüm buldum , ancak yine de 3. taraf OAuth sağlayıcısıyla gelen bir isteğin kimlik doğrulaması bağlamında görünüyor. ClientHttpRequestInterceptorHarici bir hizmete giden her bir isteğin önce bir token almasını ve ardından bu isteğe eklenmesini sağlamak için bunu a gibi bir şeyle birlikte nasıl kullanacağınız açık değildir .

Ayrıca yukarıda bağlantılı olan göç kılavuzunda OAuth2AuthorizedClientService, önleme araçlarında kullanım için yararlı olduğunu söylediği bir referansa yer verilmiştir , ancak yine ClientRegistrationRepositoryde, kullanmak istiyorsanız üçüncü taraf sağlayıcıların kayıtlarını koruduğu gibi görünen şeylere dayanıyor gibi görünüyor. gelen bir isteğin kimliğinin doğrulanmasını sağlar.

Uygulamamdan giden isteklere eklemek üzere bir belirteç almak için OAuth sağlayıcılarını kaydettirmek için bahar güvenliği 5'teki yeni işlevselliği kullanabilmemin bir yolu var mı?

Yanıtlar:


15

Spring Security 5.2.x'in OAuth 2.0 İstemci özellikleri RestTemplateyalnızca desteklemez WebClient. Bkz. Yay Güvenliği Referansı :

HTTP İstemcisi desteği

  • WebClient Servlet Ortamları için entegrasyon (korunan kaynak talep etmek için)

Ayrıca, RestTemplategelecekteki bir sürümde kullanımdan kaldırılacak. Bkz. RestTemplate javadoc :

NOT: 5.0 itibarıyla, engellemeyen, reaktif org.springframework.web.reactive.client.WebClient, RestTemplatehem senkronizasyon hem de zaman uyumsuzluğun yanı sıra akış senaryoları için verimli desteğe modern bir alternatif sunar . RestTemplateGelecekteki bir sürümde kaldırılacak ve ileriye dönük katma önemli yeni özelliklere sahip olmayacaktır. Daha WebClientfazla ayrıntı ve örnek kod için Spring Framework referans belgelerinin bölümüne bakın .

Bu nedenle, en iyi çözüm RestTemplatelehine bırakmak olacaktır WebClient.


WebClientİstemci Kimlik Bilgileri Akışı için Kullanma

İstemci kaydını ve sağlayıcısını program aracılığıyla veya Spring Boot otomatik yapılandırmasını kullanarak yapılandırın:

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

… Ve 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;
}

Örneği sağlanan ile WebClientkullanılacak ServerOAuth2AuthorizedClientExchangeFilterFunctionşekilde yapılandırın OAuth2AuthorizedClientManager:

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
            new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    oauth2Client.setDefaultClientRegistrationId("custom");
    return WebClient.builder()
            .apply(oauth2Client.oauth2Configuration())
            .build();
}

Şimdi, bu WebClientörneği kullanarak bir istekte bulunmaya çalışırsanız , ilk önce yetkilendirme sunucusundan bir belirteç isteyecek ve isteğe dahil edecektir.


Teşekkürler, bu birkaç şeyi temizler, ancak yukarıdaki tüm bağlantılı belgelerden, hala bir önleme aracının (veya yeni terminolojinin ne için WebClientolduğu) veya benzer bir şeyin bir OAuth jetonunu Giden bir isteğe eklemek için özel OAuth sağlayıcısı (Facebook / Google gibi OoTB'yi destekleyenlerden biri değil). Tüm örnekler, diğer sağlayıcılarla gelen isteklerin kimliğini doğrulamaya odaklanmış gibi görünmektedir. İyi örnekler için işaretçileriniz var mı?
Matt Williams

1
@MattWilliams Cevabı WebClient, istemci kimlik bilgileri hibe türü ile nasıl kullanılacağına dair bir örnekle güncelledim .
Anar Sultanov

Mükemmel, şimdi her şey çok daha mantıklı, çok teşekkür ederim. Birkaç gün denemek için bir şans bulamayabilirim, ancak geri döndüğümden ve bir kez
Matt Williams

1
Bu artık çok sevilmiyor ... En azından UnAuthenticatedServerOAuth2AuthorizedClientRepository ...
SledgeHammer

Teşekkürler @SledgeHammer, cevabımı güncelledim.
Anar Sultanov

1

@Anar Sultanov'un yukarıdaki cevabı bu noktaya gelmeme yardımcı oldu, ancak OAuth token isteğime bazı ek başlıklar eklemek zorunda kaldığımda, kullanım durumum için sorunu nasıl çözdüğüme dair tam bir cevap vereceğimi düşündüm.

Sağlayıcı ayrıntılarını yapılandırın

Aşağıdakileri şuraya ekle: 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:}

Özel uygulama ReactiveOAuth2AccessTokenResponseClient

Bu sunucudan sunucuya iletişim olduğundan ServerOAuth2AuthorizedClientExchangeFilterFunction. Bu sadece a'yı kabul eder ReactiveOAuth2AuthorizedClientManager, reaktif olmayanları kabul etmez OAuth2AuthorizedClientManager. Bu nedenle ReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider()(OAuth2 isteğinde bulunmak için sağlayıcıya vermek için) kullandığımızda, bunu ReactiveOAuth2AuthorizedClientProviderreaktif olmayan yerine vermek zorundayız OAuth2AuthorizedClientProvider. Gereğince yay güvenlik referans belgeleri bir kullanırsanız Reaktif olmayan DefaultClientCredentialsTokenResponseClientkullanabilirsiniz .setRequestEntityConverter()OAuth2 simgesi isteğinizi değiştirme yöntemi, ancak reaktif eşdeğer WebClientReactiveClientCredentialsTokenResponseClientbiz (biz kullanımını kendi yapabilir bizim uygulamak zorunda, bu tesis sağlamaz mevcut WebClientReactiveClientCredentialsTokenResponseClientmantık).

Uygulamam çağrıldı UaaWebClientReactiveClientCredentialsTokenResponseClient( bazı ekstra üstbilgiler / gövde alanları eklemek için varsayılandan headers()ve body()yöntemlerini çok az değiştirdiği için uygulama atlandı WebClientReactiveClientCredentialsTokenResponseClient, altta yatan kimlik doğrulama akışını değiştirmiyor).

Yapılandır WebClient

ServerOAuth2AuthorizedClientExchangeFilterFunction.setClientCredentialsTokenResponseClient()Yöntem böylece yönteminden kullanımdan kaldırılması tavsiye ardından kullanımdan kaldırıldı:

Kullanımdan kaldırıldı. Bunun yerine kullanın ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager). ClientCredentialsReactiveOAuth2AuthorizedClientProviderBir WebClientReactiveClientCredentialsTokenResponseClient(veya özel bir) ile yapılandırılmış bir örnek oluşturun ve bunu sağlayın DefaultReactiveOAuth2AuthorizedClientManager.

Bu, aşağıdaki gibi bir konfigürasyonla sonuçlanır:

@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();
}

WebClientNormal olarak kullan

oAuth2WebClientFasulye Artık diğer isteği kullanarak yapacak şekilde bizim yapılandırılmış OAuth2 sağlayıcı tarafından korunan kaynaklara erişmek için kullanılmaya hazır olduğunu WebClient.


İstemci kimliğini, istemci sırrını ve oauth uç noktasını program aracılığıyla nasıl iletirim?
monti

Bunu denemedim, ancak ClientRegistrationgerekli ayrıntılarla s örnekleri oluşturabilir ve bunları InMemoryReactiveClientRegistrationRepository(varsayılan uygulaması ReactiveClientRegistrationRepository) için yapıcıya geçirebiliriz gibi görünüyor . Daha sonra yönteme geçirilen otomatik InMemoryReactiveClientRegistrationRepositorykablolu yerine yeni oluşturulan fasulyeyi kullanıyorsunuzclientRegistrationRepositoryoauthFilteredWebClient
Matt Williams

Mh, ama ClientRegistrationçalışma zamanında farklı kayıt yapamıyorum, değil mi? Anladığım kadarıyla ClientRegistrationbaşlangıçta bir fasulye yaratmam gerekiyor .
monti

Ah tamam, sadece application.propertiesdosyada beyan etmek istemediğini sanıyordum . Kendi uygulamanızı uygulamak, ReactiveOAuth2AccessTokenResponseClientOAuth2 jetonu almak istediğiniz herhangi bir isteği yapmanıza izin verir, ancak istek başına dinamik bir "bağlam" nasıl sağlayabileceğinizi bilmiyorum.Kendi filtrenizin tamamını uyguladıysanız da aynı şey geçerlidir. Giden istek size erişim verecek, bu yüzden oradan ihtiyacınız olanı çıkartamadıkça seçeneklerinizin ne olduğundan emin değilim.Kullanım durumunuz nedir? Neden başlangıçta olası kayıtları bilmiyorsunuz?
Matt Williams

1

@Matt Williams cevap oldukça yararlı buldum. Birisi programlı olarak WebClient yapılandırması için clientId ve gizli geçmek istiyorum durumunda eklemek istiyorum. İşte böyle yapılabilir.

 @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);
    }
}

0

Merhaba belki çok geç, ancak RestTemplate hala Spring Security 5'te destekleniyor, reaktif olmayan uygulama için RestTemplate hala yapmanız gereken şey sadece bahar güvenliğini doğru bir şekilde yapılandırmak ve geçiş kılavuzunda belirtildiği gibi bir önleyici oluşturmaktır

Client_credentials akışını kullanmak için aşağıdaki yapılandırmayı kullanın

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

OauthResTemplate için yapılandırma

@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;
    }

}

önleme uçağı

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();
            }
        };
    }
}

Bu, ilk çağrıda ve belirtecin süresi dolduğunda access_token değerini oluşturur. OAuth2AuthorizedClientManager tüm bunları sizin için yönetecek

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.