सभी कॉल को संशोधित किए बिना रेट्रोफिट का उपयोग करके OAuth टोकन को ताज़ा करना


157

हम OAuth2 सुरक्षित सर्वर के साथ संवाद करने के लिए, अपने एंड्रॉइड ऐप में रेट्रोफिट का उपयोग कर रहे हैं। सब कुछ बहुत अच्छा काम करता है, हम प्रत्येक कॉल के साथ एक्सेस टोकन को शामिल करने के लिए RequestInterceptor का उपयोग करते हैं। हालांकि कई बार ऐसा होगा, जब एक्सेस टोकन समाप्त हो जाएगा, और टोकन को रीफ्रेश करने की आवश्यकता होगी। जब टोकन समाप्त हो जाता है, तो अगला कॉल अनधिकृत HTTP कोड के साथ वापस आ जाएगा, इसलिए इसे मॉनिटर करना आसान है। हम प्रत्येक रिट्रोफिट को निम्न तरीके से संशोधित कर सकते हैं: विफलता कॉलबैक में, त्रुटि कोड की जांच करें, यदि यह अनधिकृत के बराबर है, तो OAuth टोकन को ताज़ा करें, फिर रिट्रोफिट कॉल को दोहराएं। हालांकि, इसके लिए, सभी कॉल को संशोधित किया जाना चाहिए, जो आसानी से बनाए रखने योग्य और अच्छा समाधान नहीं है। क्या सभी रिट्रोफिट कॉल को संशोधित किए बिना ऐसा करने का कोई तरीका है?


1
यह मेरे अन्य प्रश्न के लिए प्रासंगिक लगता है । मैं जल्द ही फिर से इस पर गौर करूंगा, लेकिन एक संभावित दृष्टिकोण ओकेहटप्लिएंट को लपेट रहा है। कुछ इस तरह से: github.com/pakerfeldt/signpost-retrofit इसके अलावा, चूंकि मैं रिट्रोफिट के साथ रोबोस्पाइस का उपयोग कर रहा हूं, इसलिए आधार अनुरोध वर्ग बनाने के साथ-साथ एक और संभावित दृष्टिकोण हो सकता है। संभवत: आपको यह पता लगाना होगा कि कैसे एक प्रवाह के बिना अपने प्रवाह को प्राप्त करना है, शायद ओटो / ईवेंटबस का उपयोग करना।
हसन इब्राहीम

1
खैर आप इसे कांटा कर सकते हैं, और अनावश्यक मामलों को हटा सकते हैं। मैं आज शायद इस पर गौर करूंगा, और यहां पोस्ट करूंगा अगर मैंने कुछ हासिल किया जो हमारी समस्या का समाधान कर सकता है।
डैनियल जोलनै

2
यह पता चला, कि पुस्तकालय ने टोकन को ताज़ा करने का काम नहीं किया, लेकिन मुझे एक विचार दिया। मैंने कुछ अनछुए
डैनियल जोल्नाई

3
@neworld एक समाधान जो मैं सोच सकता हूं: चेंज करें यदि यह कुछ सेकंड पहले (मिलीसेकंड) हो गया है, तो टोकन को ताज़ा न करें। आप इस समयसीमा को 1 घंटे या इसके बाद सेट कर सकते हैं, ताकि टोकन के बाहर एक और समस्या होने पर नए टोकन के लिए लगातार अनुरोध करने से रोका जा सके।
डैनियल जोल्नाई

2
रिट्रोफिट 1.9.0 ने ओकेहेट्प 2.2 का समर्थन जोड़ा, जिसमें इंटरसेप्टर्स हैं। इससे आपका काम बहुत आसान हो जाना चाहिए। अधिक जानकारी के लिए, देखें: github.com/square/retrofit/blob/master/… और github.com/square/okhttp/wiki/Interceptors आपको इनके लिए भी OkHttp बढ़ाना होगा, हालाँकि।
डैनियल जोल्नाई

जवाबों:


214

कृपया Interceptorsप्रमाणीकरण से निपटने के लिए उपयोग न करें ।

वर्तमान में, प्रमाणीकरण को संभालने के लिए सबसे अच्छा तरीका नए Authenticatorएपीआई का उपयोग करना है, विशेष रूप से इस उद्देश्य के लिए डिज़ाइन किया गया है ।

जब कोई प्रतिक्रिया उनके साथ अंतिम असफल अनुरोध का पुन: प्रयास कर रही हो, तो ओक्टेट स्वचालित रूप सेAuthenticator क्रेडेंशियल्स के लिए पूछेगा401 Not Authorised

public class TokenAuthenticator implements Authenticator {
    @Override
    public Request authenticate(Proxy proxy, Response response) throws IOException {
        // Refresh your access_token using a synchronous api request
        newAccessToken = service.refreshToken();

        // Add new header to rejected request and retry it
        return response.request().newBuilder()
                .header(AUTHORIZATION, newAccessToken)
                .build();
    }

    @Override
    public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
        // Null indicates no attempt to authenticate.
        return null;
    }

एक संलग्न Authenticatorएक करने के लिए OkHttpClientएक ही तरीका है कि आप के साथ क्याInterceptors

OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setAuthenticator(authAuthenticator);

अपने बनाते समय इस क्लाइंट का उपयोग करें Retrofit RestAdapter

RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(ENDPOINT)
                .setClient(new OkClient(okHttpClient))
                .build();
return restAdapter.create(API.class);

6
क्या इसका मतलब यह है कि हर अनुरोध हमेशा 1 बार विफल होगा या क्या आप अनुरोध करते समय टोकन जोड़ते हैं?
Jdruwe

11
@Jdruwe ऐसा लगता है कि यह कोड 1 बार विफल हो जाएगा, और फिर यह अनुरोध करेगा। हालाँकि, यदि आप एक इंटरसेप्टर जोड़ते हैं, तो केवल उद्देश्य हमेशा एक पहुँच टोकन जोड़ना है (भले ही इसकी अवधि समाप्त हो या न हो) तब यह केवल तभी कहा जाएगा जब एक 401 प्राप्त होता है जो केवल उस टोकन के समाप्त होने पर होगा।
narciero

54
TokenAuthenticatorएक serviceवर्ग पर निर्भर करता है । serviceवर्ग एक पर निर्भर करता है OkHttpClientउदाहरण। OkHttpClientमैं एक की जरूरत बनाने के लिए TokenAuthenticator। मैं इस चक्र को कैसे तोड़ सकता हूं? दो अलग OkHttpClientएस? उनका अलग कनेक्शन पूल होने जा रहा है ...
ब्रिस गैबिन

6
कैसे कई समानांतर अनुरोधों के बारे में जो टोकन को ताज़ा करने की आवश्यकता है? यह एक ही समय में कई ताज़ा टोकन अनुरोध होंगे। इससे कैसे बचा जाए?
इगोर कोस्टेंको

10
ठीक है, इसलिए @ इहोर की समस्या का हल ऑथेंटिकेटर के अंदर कोड को सिंक्रनाइज़ कर सकता है। इसने मेरे मामले में समस्या हल कर दी। अनुरोध में प्रमाणित (...) विधि: - कोई भी टीकाकरण सामान करें - सिंक्रनाइज़ ब्लॉक शुरू करें (सिंक्रनाइज़ किया गया (MyAuthenticator.class) {...}) - उस ब्लॉक में वर्तमान पहुंच और ताज़ा टोकन पुनः प्राप्त करें - जांच करें कि क्या विफल अनुरोध का उपयोग नवीनतम किया गया था पहुँच टोकन (resp.request ()। शीर्ष लेख ("प्राधिकरण")) - यदि अपडेट किए गए टोकन के साथ बस इसे एक बार फिर से न चलाएं - अन्यथा ताज़ा टोकन प्रवाह - अपडेट / अपडेट अपडेट को जारी रखें और टोकन को ताज़ा करें - सिंक्रनाइज़ किया गया ब्लॉक - रेरुन
डेरियस विचेकटी

65

यदि आप Retrofit > = का उपयोग कर रहे हैं, 1.9.0तो आप OkHttp के नए इंटरसेप्टर का उपयोग कर सकते हैं , जिसे इसमें पेश किया गया था OkHttp 2.2.0। आप एक एप्लिकेशन इंटरसेप्टर का उपयोग करना चाहते हैं , जो आपको अनुमति देता है retry and make multiple calls

आपका इंटरसेप्टर कुछ इस तरह दिख सकता है:

public class CustomInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();

        // try the request
        Response response = chain.proceed(request);

        if (response shows expired token) {

            // get a new token (I use a synchronous Retrofit call)

            // create a new request and modify it accordingly using the new token
            Request newRequest = request.newBuilder()...build();

            // retry the request
            return chain.proceed(newRequest);
        }

        // otherwise just pass the original response on
        return response;
    }

}

आप अपने को परिभाषित करने के बाद Interceptor, एक बनाने के OkHttpClientलिए और एक के रूप में इंटरसेप्टर जोड़ने आवेदन इंटरसेप्टर

    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.interceptors().add(new CustomInterceptor());

और अंत में, इसे OkHttpClientबनाते समय अपना उपयोग करें RestAdapter

    RestService restService = new RestAdapter().Builder
            ...
            .setClient(new OkClient(okHttpClient))
            .create(RestService.class);

चेतावनी: जैसा कि Jesse Wilson(स्क्वायर से) यहां उल्लेख करते हैं , यह एक खतरनाक शक्ति है।

कहा जा रहा है कि, मैं निश्चित रूप से लगता है कि यह इस तरह से कुछ को संभालने के लिए सबसे अच्छा तरीका है। यदि आपके कोई प्रश्न हैं, तो कृपया टिप्पणी में पूछने में संकोच न करें।


2
जब आप एंड्रॉइड मुख्य थ्रेड पर नेटवर्क कॉल की अनुमति नहीं देते हैं तो आप एंड्रॉइड में एक तुल्यकालिक कॉल कैसे प्राप्त कर रहे हैं? मैं एक एसिंक्रोनस कॉल से रिस्पांस लौटाने में समस्याओं में चल रहा हूं।
lgdroid57

1
@ lgdroid57 आप सही हैं, इस प्रकार आपको पहले से ही एक और सूत्र पर होना चाहिए जब आपने मूल अनुरोध शुरू किया था जिसे चलाने के लिए इंटरसेप्टर को ट्रिगर किया गया था।
दिबांग

3
इसने बहुत अच्छा काम किया, सिवाय इसके कि मुझे पिछली प्रतिक्रिया को बंद करना था या मैं पिछले कनेक्शन को लीक कर दूंगा ... अंतिम अनुरोध newRequest = request.newBuilder () .... निर्माण (); । response.body () पास (); वापसी श्रृंखला। सुधार (newRequest);
डैलिनडायर

धन्यवाद! मैं एक ऐसे मुद्दे पर चल रहा था जहां मूल अनुरोध के कॉलबैक को मूल प्रतिक्रिया के बजाय "बंद" का एक त्रुटि संदेश प्राप्त हो रहा था, क्योंकि इंटरसेप्टर में शरीर की खपत हो रही थी। मैं सफल प्रतिक्रियाओं के लिए इसे ठीक करने में सक्षम था, लेकिन मैं असफल प्रतिक्रियाओं के लिए इसे ठीक करने में सक्षम नहीं था। कोई सुझाव?
lgdroid57

धन्यवाद @mattblang, यह अच्छा लग रहा है। एक प्रश्न: क्या अनुरोध कॉलबैक को पुनः प्राप्ति पर भी कॉल करने की गारंटी है?
ल्यूका फागियोली

23

TokenAuthenticator एक सेवा वर्ग पर निर्भर करता है। सेवा वर्ग एक OkHttpClient उदाहरण पर निर्भर करता है। एक OkHttpClient बनाने के लिए मुझे TokenAuthenticator की आवश्यकता है। मैं इस चक्र को कैसे तोड़ सकता हूं? दो अलग-अलग OkHttpClients? वे अलग कनेक्शन पूल के लिए जा रहे हैं ..

यदि आपके पास एक ऐसा रिट्रोफिट है, TokenServiceजिसकी आपको आवश्यकता है, Authenticatorलेकिन आप केवल उसी को सेट करना चाहेंगे, OkHttpClientजिसका उपयोग आप TokenServiceHolderएक आश्रित के रूप में कर सकते हैं TokenAuthenticator। आपको एप्लिकेशन (सिंगलटन) स्तर पर इसके संदर्भ को बनाए रखना होगा। यदि आप डैगर 2 का उपयोग कर रहे हैं तो यह आसान है, अन्यथा केवल अपने एप्लिकेशन के अंदर क्लास फ़ील्ड बनाएं।

में TokenAuthenticator.java

public class TokenAuthenticator implements Authenticator {

    private final TokenServiceHolder tokenServiceHolder;

    public TokenAuthenticator(TokenServiceHolder tokenServiceHolder) {
        this.tokenServiceHolder = tokenServiceHolder;
    }

    @Override
    public Request authenticate(Proxy proxy, Response response) throws IOException {

        //is there a TokenService?
        TokenService service = tokenServiceHolder.get();
        if (service == null) {
            //there is no way to answer the challenge
            //so return null according to Retrofit's convention
            return null;
        }

        // Refresh your access_token using a synchronous api request
        newAccessToken = service.refreshToken().execute();

        // Add new header to rejected request and retry it
        return response.request().newBuilder()
                .header(AUTHORIZATION, newAccessToken)
                .build();
    }

    @Override
    public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
        // Null indicates no attempt to authenticate.
        return null;
    }

इन TokenServiceHolder.java:

public class TokenServiceHolder {

    TokenService tokenService = null;

    @Nullable
    public TokenService get() {
        return tokenService;
    }

    public void set(TokenService tokenService) {
        this.tokenService = tokenService;
    }
}

ग्राहक सेटअप:

//obtain instance of TokenServiceHolder from application or singleton-scoped component, then
TokenAuthenticator authenticator = new TokenAuthenticator(tokenServiceHolder);
OkHttpClient okHttpClient = new OkHttpClient();    
okHttpClient.setAuthenticator(tokenAuthenticator);

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .client(okHttpClient)
    .build();

TokenService tokenService = retrofit.create(TokenService.class);
tokenServiceHolder.set(tokenService);

यदि आप डैगर 2 या एक समान निर्भरता इंजेक्शन ढांचे का उपयोग कर रहे हैं, तो इस प्रश्न के उत्तर में कुछ उदाहरण हैं


TokenServiceकक्षा कहाँ बनाई गई है?
योगेश सुथार

@YogeshSuthar यह एक रेट्रोफिट सेवा है - संबंधित प्रश्न
डेविड रावसन

धन्यवाद, आप के कार्यान्वयन प्रदान कर सकते हैं refreshToken()से service.refreshToken().execute();। कहीं भी यह लागू नहीं हो पा रहा है।
योगेश सुथार

@ योगेश ताज़ा तरीका आपके एपीआई से है। जो भी आप एक टोकन ताज़ा करने के लिए कहते हैं (उपयोगकर्ता नाम और पासवर्ड के साथ एक कॉल?)। या शायद एक अनुरोध जहां आप एक टोकन जमा करते हैं और प्रतिक्रिया एक नया टोकन है
डेविड रॉसन

5

TokenAuthenticator@Theblang उत्तर की तरह उपयोग करना संभाल के लिए एक सही तरीका है refresh_token

यहाँ मेरा कार्यान्वयन है (मुझे कोटलिन, डैगर, आरएक्स का उपयोग करना है लेकिन आप इस विचार का उपयोग अपने मामले पर लागू करने के लिए कर सकते हैं)
TokenAuthenticator

class TokenAuthenticator @Inject constructor(private val noneAuthAPI: PotoNoneAuthApi, private val accessTokenWrapper: AccessTokenWrapper) : Authenticator {

    override fun authenticate(route: Route, response: Response): Request? {
        val newAccessToken = noneAuthAPI.refreshToken(accessTokenWrapper.getAccessToken()!!.refreshToken).blockingGet()
        accessTokenWrapper.saveAccessToken(newAccessToken) // save new access_token for next called
        return response.request().newBuilder()
                .header("Authorization", newAccessToken.token) // just only need to override "Authorization" header, don't need to override all header since this new request is create base on old request
                .build()
    }
}

@Brais Gabin टिप्पणी जैसे निर्भरता चक्र को रोकने के लिए , मैं 2 इंटरफ़ेस बनाता हूं जैसे

interface PotoNoneAuthApi { // NONE authentication API
    @POST("/login")
    fun login(@Body request: LoginRequest): Single<AccessToken>

    @POST("refresh_token")
    @FormUrlEncoded
    fun refreshToken(@Field("refresh_token") refreshToken: String): Single<AccessToken>
}

तथा

interface PotoAuthApi { // Authentication API
    @GET("api/images")
    fun getImage(): Single<GetImageResponse>
}

AccessTokenWrapper कक्षा

class AccessTokenWrapper constructor(private val sharedPrefApi: SharedPrefApi) {
    private var accessToken: AccessToken? = null

    // get accessToken from cache or from SharePreference
    fun getAccessToken(): AccessToken? {
        if (accessToken == null) {
            accessToken = sharedPrefApi.getObject(SharedPrefApi.ACCESS_TOKEN, AccessToken::class.java)
        }
        return accessToken
    }

    // save accessToken to SharePreference
    fun saveAccessToken(accessToken: AccessToken) {
        this.accessToken = accessToken
        sharedPrefApi.putObject(SharedPrefApi.ACCESS_TOKEN, accessToken)
    }
}

AccessToken कक्षा

data class AccessToken(
        @Expose
        var token: String,

        @Expose
        var refreshToken: String)

मेरा इंटरसेप्टर

class AuthInterceptor @Inject constructor(private val accessTokenWrapper: AccessTokenWrapper): Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val authorisedRequestBuilder = originalRequest.newBuilder()
                .addHeader("Authorization", accessTokenWrapper.getAccessToken()!!.token)
                .header("Accept", "application/json")
        return chain.proceed(authorisedRequestBuilder.build())
    }
}

अंत में, जोड़ने Interceptorऔर Authenticatorअपने को OKHttpClientजब बनाने सेवा PotoAuthApi

डेमो

https://github.com/PhanVanLinh/AndroidMVPKotlin

ध्यान दें

प्रामाणिक प्रवाह
  • उदाहरण एपीआई getImage()वापसी 401 त्रुटि कोड
  • authenticateअंदर की विधि निकाल दीTokenAuthenticator जाएगी
  • संक्रान्ति noneAuthAPI.refreshToken(...)कहा जाता है
  • noneAuthAPI.refreshToken(...)प्रतिक्रिया के बाद -> नया टोकन हेडर में जोड़ देगा
  • getImage()क्या ऑटो को नए हेडर के साथ बुलाया जाएगा ( इस कॉल को HttpLogging लॉग इन नहीं किया जाएगा ) ( interceptअंदर कॉल AuthInterceptor नहीं होगी )
  • यदि getImage()अभी भी त्रुटि 401 के साथ विफल हो गई है, तो authenticateअंदर की विधि AGAIN और AGAINTokenAuthenticator को निकाल देगी तो यह कई बार कॉल विधि के बारे में त्रुटि फेंक देगा ( java.net.ProtocolException: Too many follow-up requests)। आप इसे प्रतिसाद प्रतिक्रिया से रोक सकते हैं । उदाहरण के लिए, यदि आप return nullमें authenticateके बाद 3 बार पुन: प्रयास, getImage()होगा खत्म औरreturn response 401

  • यदि getImage()प्रतिक्रिया सफलता => हम सामान्य रूप से परिणाम प्राप्त करेंगे (जैसे आप getImage()बिना किसी त्रुटि के कॉल करते हैं)

आशा है कि यह मदद करेगा


यह समाधान आपके ServiceGenerator वर्ग में स्पष्ट रूप से 2 विभिन्न OkHttpClients का उपयोग करता है।
स्पेशलस्लोफ्लेक

@SpecialSnowflake आप सही हैं। यदि आप मेरे समाधान का पालन करते हैं, तो आपको 2 OkHttp बनाने की आवश्यकता है क्योंकि मैंने 2 सेवा (oauth और कोई नहीं) बनाई है। मुझे लगता है कि इससे कोई समस्या नहीं होगी। मुझे अपना विचार बताइए
फान वान

1

मैं इसे एक पुराना धागा जानता हूं, लेकिन अगर किसी ने इसमें ठोकर खाई है तो बस।

TokenAuthenticator एक सेवा वर्ग पर निर्भर करता है। सेवा वर्ग एक OkHttpClient उदाहरण पर निर्भर करता है। एक OkHttpClient बनाने के लिए मुझे TokenAuthenticator की आवश्यकता है। मैं इस चक्र को कैसे तोड़ सकता हूं? दो अलग-अलग OkHttpClients? वे अलग कनेक्शन पूल के लिए जा रहे हैं ..

मैं एक ही समस्या का सामना कर रहा था, लेकिन मैं केवल एक ही OkHttpClient becuase बनाना चाहता था, मुझे नहीं लगता कि मुझे केवल एक ही टोकनएथुएंटिसिकेटर के लिए एक और की आवश्यकता है, मैं Dagger2 का उपयोग कर रहा था, इसलिए मैंने सेवा वर्ग प्रदान किया क्योंकि लेज़ी इंजेक्ट किया गया। TokenAuthenticator, आप यहाँ खंजर 2 में आलसी इंजेक्शन के बारे में अधिक पढ़ सकते हैं , लेकिन यह मूल रूप से Dagger को जाने और टोकनेनथेंटिकेटर द्वारा आवश्यक सेवा को तुरंत बनाने के लिए कह रहा है।

आप नमूना कोड के लिए इस SO थ्रेड का उल्लेख कर सकते हैं: Dagger2 का उपयोग करते समय एक परिपत्र निर्भरता कैसे हल करें?


0

आप अपने सभी लोडर के लिए एक बेस क्लास बनाने की कोशिश कर सकते हैं जिसमें आप एक विशेष अपवाद को पकड़ने में सक्षम होंगे और फिर अपनी आवश्यकतानुसार कार्य करेंगे। व्यवहार को फैलाने के लिए अपने सभी अलग-अलग लोडर को बेस क्लास से बढ़ाएं।


रेट्रोफिट उस तरह से काम नहीं करता है। यह एक एपीआई कॉल का वर्णन करने के लिए जावा एनोटेशन, और इंटरफेस का उपयोग करता है
डैनियल ज़ोलनाई

मुझे पता है कि रेट्रोफ़िट कैसे काम करता है, लेकिन आप अभी भी अपने एपीआई कॉल को "रैपिंग" कर रहे हैं, क्या आप असिनटस्क के अंदर नहीं हैं?
k3v1n4ud3

नहीं, मैं कॉलबैक के साथ कॉल का उपयोग करता हूं, इसलिए वे अतुल्यकालिक रूप से चलते हैं।
डैनियल ज़ोलनई

तब आप शायद आधार कॉलबैक क्लास बना सकते हैं और अपने सभी कॉलबैक को बढ़ा सकते हैं।
k3v1n4ud3

2
इसका कोई हल? बिल्कुल यहाँ मेरा मामला है। = /
ह्यूगो नोगीरा

0

लंबे शोध के बाद, मैंने अपाचे क्लाइंट को रीफ्रेशिंग एक्सेसटोकन फॉर रिट्रोफिट को संभालने के लिए अनुकूलित किया जिसमें आप पैरामीटर के रूप में एक्सेस टोकन भेजते हैं।

कुकी स्थायी ग्राहक के साथ अपने एडाप्टर को आरंभ करें

restAdapter = new RestAdapter.Builder()
                .setEndpoint(SERVER_END_POINT)
                .setClient(new CookiePersistingClient())
                .setLogLevel(RestAdapter.LogLevel.FULL).build();

कुकी स्थायी ग्राहक जो सभी अनुरोधों के लिए कुकीज़ रखता है और प्रत्येक अनुरोध की प्रतिक्रिया के साथ जांच करता है, अगर यह अनधिकृत पहुंच ERROR_CODE = 401 है, तो पहुंच टोकन को ताज़ा करें और अनुरोध को याद रखें, अन्यथा सिर्फ अनुरोध अनुरोध करता है।

private static class CookiePersistingClient extends ApacheClient {

    private static final int HTTPS_PORT = 443;
    private static final int SOCKET_TIMEOUT = 300000;
    private static final int CONNECTION_TIMEOUT = 300000;

    public CookiePersistingClient() {
        super(createDefaultClient());
    }

    private static HttpClient createDefaultClient() {
        // Registering https clients.
        SSLSocketFactory sf = null;
        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore
                    .getDefaultType());
            trustStore.load(null, null);

            sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        HttpParams params = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(params,
                CONNECTION_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", sf, HTTPS_PORT));
        // More customization (https / timeouts etc) can go here...

        ClientConnectionManager cm = new ThreadSafeClientConnManager(
                params, registry);
        DefaultHttpClient client = new DefaultHttpClient(cm, params);

        // Set the default cookie store
        client.setCookieStore(COOKIE_STORE);

        return client;
    }

    @Override
    protected HttpResponse execute(final HttpClient client,
            final HttpUriRequest request) throws IOException {
        // Set the http context's cookie storage
        BasicHttpContext mHttpContext = new BasicHttpContext();
        mHttpContext.setAttribute(ClientContext.COOKIE_STORE, COOKIE_STORE);
        return client.execute(request, mHttpContext);
    }

    @Override
    public Response execute(final Request request) throws IOException {
        Response response = super.execute(request);
        if (response.getStatus() == 401) {

            // Retrofit Callback to handle AccessToken
            Callback<AccessTockenResponse> accessTokenCallback = new Callback<AccessTockenResponse>() {

                @SuppressWarnings("deprecation")
                @Override
                public void success(
                        AccessTockenResponse loginEntityResponse,
                        Response response) {
                    try {
                        String accessToken =  loginEntityResponse
                                .getAccessToken();
                        TypedOutput body = request.getBody();
                        ByteArrayOutputStream byte1 = new ByteArrayOutputStream();
                        body.writeTo(byte1);
                        String s = byte1.toString();
                        FormUrlEncodedTypedOutput output = new FormUrlEncodedTypedOutput();
                        String[] pairs = s.split("&");
                        for (String pair : pairs) {
                            int idx = pair.indexOf("=");
                            if (URLDecoder.decode(pair.substring(0, idx))
                                    .equals("access_token")) {
                                output.addField("access_token",
                                        accessToken);
                            } else {
                                output.addField(URLDecoder.decode(
                                        pair.substring(0, idx), "UTF-8"),
                                        URLDecoder.decode(
                                                pair.substring(idx + 1),
                                                "UTF-8"));
                            }
                        }
                        execute(new Request(request.getMethod(),
                                request.getUrl(), request.getHeaders(),
                                output));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }

                @Override
                public void failure(RetrofitError error) {
                    // Handle Error while refreshing access_token
                }
            };
            // Call Your retrofit method to refresh ACCESS_TOKEN
            refreshAccessToken(GRANT_REFRESH,CLIENT_ID, CLIENT_SECRET_KEY,accessToken, accessTokenCallback);
        }

        return response;
    }
}

क्या कोई कारण है कि आप सुझाए गए समाधान के बजाय ApacheClient का उपयोग कर रहे हैं? ऐसा नहीं है कि यह एक अच्छा समाधान नहीं है, लेकिन इंटरसेप्टर्स का उपयोग करने की तुलना में इसे बहुत अधिक कोडिंग की आवश्यकता है।
डैनियल ज़ोलनई

कुकी कस्टमाइज्ड होने के लिए इसका कस्टमाइज़, पूरे सेवाओं के दौरान सेशन बनाए रखता है। यहां तक ​​कि रिक्वेस्ट इंटरसेप्टर में, आप हेडर्स में एक्सीस्टोकन जोड़ सकते हैं। लेकिन अगर आप इसे एक परम के रूप में जोड़ना चाहते हैं तो क्या होगा? इसके अलावा OKHTTPClient की सीमाएँ हैं। रेफरी: stackoverflow.com/questions/24594823/…
सुनील प्रकाश

इसे किसी भी स्थिति में उपयोग करने के लिए अधिक सामान्यीकृत किया गया है। 1. कुकी स्थायी ग्राहक 2. HTTP और HTTPS अनुरोधों को स्वीकार करता है। 3. Params में अपडेट टोकन प्राप्त करें।
सुनील प्रकाश

0

एक इंटरसेप्टर (टोकन इंजेक्षन) और एक ऑथेंटिकेटर (रिफ्रेश ऑपरेशन) का उपयोग कर काम करते हैं लेकिन:

मुझे दोहरी कॉल समस्या भी थी: पहला कॉल हमेशा 401 लौटाता था: पहली कॉल (इंटरसेप्टर) पर टोकन इंजेक्ट नहीं किया गया था और प्रमाणीकरणकर्ता को बुलाया गया था: दो अनुरोध किए गए थे।

तय करने के लिए इंटरसेप्टर में निर्माण के अनुरोध को फिर से प्राप्त करना था:

इससे पहले:

private Interceptor getInterceptor() {
    return (chain) -> {
        Request request = chain.request();
        //...
        request.newBuilder()
                .header(AUTHORIZATION, token))
                .build();
        return chain.proceed(request);
    };
}

उपरांत:

private Interceptor getInterceptor() {
    return (chain) -> {
        Request request = chain.request();
        //...
        request = request.newBuilder()
                .header(AUTHORIZATION, token))
                .build();
        return chain.proceed(request);
    };
}

एक ब्लॉक में:

private Interceptor getInterceptor() {
    return (chain) -> {
        Request request = chain.request().newBuilder()
                .header(AUTHORIZATION, token))
                .build();
        return chain.proceed(request);
    };
}

आशा करता हूँ की ये काम करेगा।

संपादित करें: मुझे पहली बार कॉल करने से बचने का कोई तरीका नहीं मिला, केवल प्रमाणिकताकर्ता और कोई इंटरसेप्टर का उपयोग करके 401 को वापस करने के लिए


-2

टोकन रीफ्रेश करने पर समवर्ती / समानांतर कॉल को हल करना चाहते थे। यहाँ एक समाधान है

class TokenAuthenticator: Authenticator {

    override fun authenticate(route: Route?, response: Response?): Request? {
        response?.let {
            if (response.code() == 401) {
                while (true) {
                    if (!isRefreshing) {
                        val requestToken = response.request().header(AuthorisationInterceptor.AUTHORISATION)
                        val currentToken = OkHttpUtil.headerBuilder(UserService.instance.token)

                        currentToken?.let {
                            if (requestToken != currentToken) {
                                return generateRequest(response, currentToken)
                            }
                        }

                        val token = refreshToken()
                        token?.let {
                            return generateRequest(response, token)
                        }
                    }
                }
            }
        }

        return null
    }

    private fun generateRequest(response: Response, token: String): Request? {
        return response.request().newBuilder()
                .header(AuthorisationInterceptor.USER_AGENT, OkHttpUtil.UA)
                .header(AuthorisationInterceptor.AUTHORISATION, token)
                .build()
    }

    private fun refreshToken(): String? {
        synchronized(TokenAuthenticator::class.java) {
            UserService.instance.token?.let {
                isRefreshing = true

                val call = ApiHelper.refreshToken()
                val token = call.execute().body()
                UserService.instance.setToken(token, false)

                isRefreshing = false

                return OkHttpUtil.headerBuilder(token)
            }
        }

        return null
    }

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