OAuth2RestTemplate के लिए स्प्रिंग सुरक्षा 5 प्रतिस्थापन


14

में spring-security-oauth2:2.4.0.RELEASEइस तरह के रूप कक्षाएं OAuth2RestTemplate, OAuth2ProtectedResourceDetailsऔर ClientCredentialsAccessTokenProviderसभी के रूप में पदावनत चिह्नित किया गया है।

इन वर्गों पर javadoc से यह एक वसंत सुरक्षा प्रवासन गाइड की ओर इशारा करता है जो बताता है कि लोगों को कोर वसंत-सुरक्षा 5 परियोजना में स्थानांतरित करना चाहिए। हालांकि मुझे यह पता लगाने में परेशानी हो रही है कि मैं इस परियोजना में अपने उपयोग के मामले को कैसे लागू करूंगा।

यदि आप अपने आवेदन के लिए आने वाले अनुरोधों को प्रमाणित करना चाहते हैं और आप पहचान को सत्यापित करने के लिए तीसरे पक्ष के OAuth प्रदाता का उपयोग करना चाहते हैं, तो दस्तावेज़ीकरण और उदाहरण के सभी एक 3 भाग OAuth प्रदाता के साथ एकीकृत करने के बारे में बात करते हैं।

मेरे उपयोग के मामले में मैं जो करना चाहता हूं वह RestTemplateएक बाहरी सेवा के साथ एक अनुरोध करना है जो OAuth द्वारा संरक्षित है। वर्तमान में मैं OAuth2ProtectedResourceDetailsअपनी क्लाइंट आईडी और सीक्रेट बनाता हूं, जिसे मैं पास करता हूं OAuth2RestTemplate। मैं भी एक कस्टम है ClientCredentialsAccessTokenProviderको जोड़ा गया OAuth2ResTemplateहै कि बस टोकन अनुरोध है कि OAuth प्रदाता मैं उपयोग कर रहा हूँ के लिए आवश्यक हैं करने के लिए कुछ अतिरिक्त हेडर कहते हैं।

स्प्रिंग-सिक्योरिटी 5 डॉक्यूमेंटेशन में मैंने एक सेक्शन पाया है जिसमें टोकन रिक्वेस्ट को कस्टमाइज़ करने का जिक्र है , लेकिन फिर से वह थर्ड पार्टी OAuth प्रोवाइडर के साथ इनकमिंग रिक्वेस्ट को प्रमाणित करने के संदर्भ में दिखता है। यह स्पष्ट नहीं है कि आप इसका उपयोग किसी चीज के साथ संयोजन के रूप में करेंगे ClientHttpRequestInterceptorताकि यह सुनिश्चित किया जा सके कि बाहरी सेवा के प्रत्येक निवर्तमान अनुरोध को पहले एक टोकन मिलता है और फिर उस अनुरोध में जोड़ा जाता है।

इसके अलावा ऊपर दिए गए माइग्रेशन गाइड में एक संदर्भ है OAuth2AuthorizedClientServiceजो यह कहता है कि यह इंटरसेप्टर में उपयोग करने के लिए उपयोगी है, लेकिन फिर से यह ऐसा दिखता है जैसे यह उन चीजों पर निर्भर करता है, ClientRegistrationRepositoryजो ऐसा लगता है जहां यह तीसरे पक्ष के प्रदाताओं के लिए पंजीकरण बनाए रखता है यदि आप उपयोग करना चाहते हैं यह सुनिश्चित करने के लिए कि आने वाले अनुरोध को प्रमाणित किया जाए।

क्या मेरे आवेदन से आउटगोइंग अनुरोधों को जोड़ने के लिए टोकन प्राप्त करने के लिए OAuth प्रदाताओं को पंजीकृत करने के लिए वसंत-सुरक्षा 5 में नई कार्यक्षमता का उपयोग करने का कोई तरीका है?

जवाबों:


15

OAuth 2.0 स्प्रिंग सिक्योरिटी 5.2.x के क्लाइंट फीचर्स का समर्थन नहीं करते हैं RestTemplate, लेकिन केवल WebClient। देखें वसंत सुरक्षा संदर्भ :

HTTP क्लाइंट का समर्थन

  • WebClient सर्वलेट वातावरण के लिए एकीकरण (संरक्षित संसाधनों का अनुरोध करने के लिए)

इसके अलावा, RestTemplateभविष्य के संस्करण में पदावनत किया जाएगा। RestTemplate javadoc देखें :

नोट: 5.0 के रूप में, गैर-अवरुद्ध, प्रतिक्रियाशील सिंक और एसिंक्स दोनों के साथ-साथ स्ट्रीमिंग परिदृश्यों के लिए कुशल समर्थन org.springframework.web.reactive.client.WebClientके RestTemplateसाथ एक आधुनिक विकल्प प्रदान करता है। RestTemplateएक भविष्य संस्करण में हटा दिया जाएगा और प्रमुख नई सुविधाओं के लिए नहीं होगा आगे जाने गयी। WebClientअधिक विवरण और उदाहरण कोड के लिए स्प्रिंग फ्रेमवर्क संदर्भ दस्तावेज का अनुभाग देखें ।

इसलिए, सबसे अच्छा समाधान के RestTemplateपक्ष में छोड़ देना होगा WebClient


WebClientक्लाइंट क्रेडेंशियल फ़्लो के लिए उपयोग करना

क्लाइंट पंजीकरण और प्रदाता को प्रोग्रामेटिक रूप से कॉन्फ़िगर करें या स्प्रिंग बूट ऑटो-कॉन्फ़िगरेशन का उपयोग करें:

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

… और 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;
}

कॉन्फ़िगर WebClientका उपयोग करने के उदाहरण ServerOAuth2AuthorizedClientExchangeFilterFunctionप्रदान की साथ OAuth2AuthorizedClientManager:

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

अब, यदि आप इस WebClientउदाहरण का उपयोग करके अनुरोध करने का प्रयास करते हैं , तो यह पहले प्राधिकरण सर्वर से एक टोकन का अनुरोध करेगा और इसे अनुरोध में शामिल करेगा।


धन्यवाद, जो कुछ चीजों को साफ करता है, लेकिन ऊपर के सभी लिंक किए गए डॉक्यूमेंटेशन से मैं अभी भी एक उदाहरण ढूंढने के लिए संघर्ष कर रहा हूं जहां एक इंटरसेप्टर (या जो भी नई शब्दावली WebClientहै) या कुछ इसी तरह का उपयोग OAuth टोकन लाने के लिए किया जाता है कस्टम OAuth प्रदाता (इनमें से कोई भी OoTB जैसे फेसबुक / Google का समर्थन नहीं करता है) एक निवर्तमान अनुरोध पर इसे जोड़ने के लिए। सभी उदाहरण अन्य प्रदाताओं के साथ आने वाले अनुरोधों को प्रमाणित करने पर ध्यान केंद्रित करते हैं। क्या आपको किसी अच्छे उदाहरण के लिए कोई संकेत मिला है?
मैट विलियम्स

1
@MattWilliams मैंने WebClientक्लाइंट क्रेडेंशियल्स अनुदान प्रकार के साथ उपयोग करने के एक उदाहरण के साथ उत्तर अपडेट किया ।
अनार सुल्तानोव

बिल्कुल सही, यह सब बहुत अधिक समझ में आता है, बहुत बहुत धन्यवाद। मुझे कुछ दिनों के लिए इसे आज़माने का मौका नहीं मिल सकता है, लेकिन एक बार जब मैं जाऊंगा तो सही उत्तर के रूप में वापस आना और इसे चिह्नित करना सुनिश्चित करूंगा
मैट विलियम्स

1
कि अब बहुत ज्यादा वंचित है ... कम से कम UnAuthenticatedServerOAuth2AuthorizedClientRepository है ...
Sange Hammer

धन्यवाद @Sange Hammer, मैंने अपना उत्तर अपडेट किया।
अनार सुल्तानोव

1

@ अर्न सुल्तानोव के उपरोक्त उत्तर ने मुझे इस मुकाम तक पहुंचाने में मदद की, लेकिन जैसा कि मुझे अपने OAuth टोकन अनुरोध में कुछ अतिरिक्त हेडर जोड़ने थे, मुझे लगा कि मैं इस बात का पूरा जवाब दूंगा कि मैंने अपने उपयोग के मामले को कैसे हल किया।

प्रदाता विवरण कॉन्फ़िगर करें

करने के लिए निम्नलिखित जोड़ें 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:}

कस्टम को लागू करें ReactiveOAuth2AccessTokenResponseClient

जैसा कि यह सर्वर-से-सर्वर संचार है हमें इसका उपयोग करने की आवश्यकता है ServerOAuth2AuthorizedClientExchangeFilterFunction। यह केवल ReactiveOAuth2AuthorizedClientManagerगैर-प्रतिक्रियाशील को स्वीकार करता है OAuth2AuthorizedClientManager। इसलिए जब हम उपयोग करते हैं ReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider()(OAuth2 अनुरोध करने के लिए उपयोग करने के लिए प्रदाता को देने के लिए) तो हमें इसे ReactiveOAuth2AuthorizedClientProviderगैर-प्रतिक्रियाशील के बजाय देना होगा OAuth2AuthorizedClientProvider। के अनुसार वसंत-सुरक्षा संदर्भ दस्तावेज़ यदि आप एक का उपयोग गैर प्रतिक्रियाशील DefaultClientCredentialsTokenResponseClientआप उपयोग कर सकते .setRequestEntityConverter()OAuth2 टोकन अनुरोध को बदलने के लिए विधि है, लेकिन प्रतिक्रियाशील बराबर WebClientReactiveClientCredentialsTokenResponseClientयह सुविधा प्रदान नहीं करता है, तो हम अपने ही (हम का उपयोग कर सकते लागू करना मौजूदा WebClientReactiveClientCredentialsTokenResponseClientतर्क)।

मेरे कार्यान्वयन को बुलाया गया UaaWebClientReactiveClientCredentialsTokenResponseClient(कार्यान्वयन को छोड़ दिया गया क्योंकि यह केवल कुछ अतिरिक्त हेडर / बॉडी फ़ील्ड को जोड़ने के लिए डिफ़ॉल्ट से बहुत थोड़ा बदल जाता है headers()और body()विधियाँ WebClientReactiveClientCredentialsTokenResponseClientहैं, यह अंतर्निहित स्थिति प्रवाह को नहीं बदलता है)।

कॉन्फ़िगर WebClient

ServerOAuth2AuthorizedClientExchangeFilterFunction.setClientCredentialsTokenResponseClient()विधि बहिष्कृत कर दिया गया है, ताकि विधि से प्रतिवाद सलाह निम्नलिखित:

पदावनत। इसके बजाय उपयोग करें ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)। (या एक कस्टम एक) के ClientCredentialsReactiveOAuth2AuthorizedClientProviderसाथ कॉन्फ़िगर करने WebClientReactiveClientCredentialsTokenResponseClientऔर इसे आपूर्ति करने की तुलना में एक उदाहरण बनाएं DefaultReactiveOAuth2AuthorizedClientManager

यह कॉन्फ़िगरेशन कुछ इस तरह दिख रहा है:

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

WebClientसामान्य के रूप में उपयोग करें

oAuth2WebClientसेम अब उन संसाधनों तक पहुंच जिस तरह से आप किसी अन्य अनुरोध एक का उपयोग कर बनाना होगा में हमारे लिए कॉन्फ़िगर किया गया OAuth2 प्रदाता द्वारा संरक्षित करने के लिए इस्तेमाल किया जा करने के लिए तैयार है WebClient


मैं क्लाइंट-आईडी, क्लाइंट-सीक्रेट और एक ऑउथ एंडपॉइंट प्रोग्राम को कैसे पारित कर सकता हूं?
मोंटी

मैंने यह कोशिश नहीं की है, लेकिन ऐसा लगता है कि आप ClientRegistrationआवश्यक विवरणों के साथ उदाहरणों का निर्माण कर सकते हैं और उन्हें InMemoryReactiveClientRegistrationRepository(डिफ़ॉल्ट कार्यान्वयन के लिए ReactiveClientRegistrationRepository) कंस्ट्रक्टर में पास कर सकते हैं । फिर आप InMemoryReactiveClientRegistrationRepositoryमेरे ऑटोबायर्ड के स्थान पर उस नव निर्मित बीन का उपयोग करें जिसे विधि clientRegistrationRepositoryमें पारित किया गया oauthFilteredWebClientहै
मैट विलियम्स

Mh, लेकिन मैं ClientRegistrationरनटाइम में अलग-अलग पंजीकरण नहीं कर पा रहा हूं , क्या मैं हूं? जहां तक ​​मुझे समझ में आया कि मुझे ClientRegistrationस्टार्टअप पर सेम बनाने की जरूरत है ।
मोंटी

आह ठीक है, मैंने सोचा कि आप उन्हें application.propertiesफ़ाइल में घोषित नहीं करना चाहते थे । अपने स्वयं के कार्यान्वयन से ReactiveOAuth2AccessTokenResponseClientआप OAuth2 टोकन प्राप्त करने के लिए जो भी अनुरोध करना चाहते हैं, कर सकते हैं, लेकिन मुझे नहीं पता कि आप इसे प्रति अनुरोध पर एक गतिशील "संदर्भ 'कैसे प्रदान कर सकते हैं। यदि आप अपने स्वयं के संपूर्ण फ़िल्टर को लागू करते हैं, तो यह वही होगा। आपको आउटगोइंग अनुरोध तक पहुंच प्रदान करेगा, इसलिए जब तक आप अनुमान लगा सकते हैं कि आपको वहां से क्या चाहिए, मुझे यकीन नहीं है कि आपके विकल्प क्या हैं। आपका उपयोग मामला क्या है? आप स्टार्टअप पर संभावित पंजीकरण क्यों नहीं जान पाएंगे?
मैट विलियम्स

1

मैंने पाया @ मट्ट विलियम्स का जवाब काफी मददगार है। हालाँकि मैं किसी ऐसे मामले में जोड़ना चाहूँगा जो कोई व्यक्ति क्लाइंटलाइड और वेबक्लाइंट कॉन्फ़िगरेशन के लिए गुप्त रूप से पास करना चाहे। यहां बताया गया है कि यह कैसे किया जा सकता है।

 @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

हाय शायद यह बहुत देर हो चुकी है फिर भी RestTemplate स्प्रिंग सिक्योरिटी 5 में समर्थित है, गैर-प्रतिक्रियाशील ऐप के लिए RestTemplate अभी भी उपयोग किया जाता है जो आपको करना है केवल वसंत सुरक्षा को ठीक से कॉन्फ़िगर करना है और माइग्रेशन गाइड पर उल्लिखित इंटरसेप्टर बनाना है

क्लाइंट कॉन्फ़िगरेशन का उपयोग करने के लिए निम्न कॉन्फ़िगरेशन का उपयोग करें

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 के लिए कॉन्फ़िगरेशन

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

}

इंटरसेप्टर

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

यह पहली कॉल में access_token उत्पन्न करेगा और जब भी टोकन की समय सीमा समाप्त होगी। OAuth2AuthorizedClientManager आपके लिए यह सब प्रबंधित करेगा

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.