সমস্ত কল পরিবর্তন না করেই রেট্রোফিট ব্যবহার করে ওআউথ টোকেনকে রিফ্রেশ করছে


157

ওআউথ 2 সুরক্ষিত সার্ভারের সাথে যোগাযোগ করার জন্য আমরা আমাদের অ্যান্ড্রয়েড অ্যাপে রেট্রোফিট ব্যবহার করছি। সবকিছু দুর্দান্ত কাজ করে, আমরা প্রতিটি কলের সাথে অ্যাক্সেস টোকেন অন্তর্ভুক্ত করার জন্য আমরা অনুরোধপ্রযুক্তি ব্যবহার করি। তবে এমন সময় আসবে যখন অ্যাক্সেস টোকেনটির মেয়াদ শেষ হয়ে যাবে এবং টোকেনটি রিফ্রেশ করা দরকার। টোকেনটির মেয়াদ শেষ হয়ে গেলে, পরবর্তী কলটি অননুমোদিত HTTP কোড সহ ফিরে আসবে, যাতে এটি নিরীক্ষণ করা সহজ। আমরা প্রতিটি রেট্রোফিট কলকে নিম্নলিখিত উপায়ে সংশোধন করতে পারি: ব্যর্থতা কলব্যাকে ত্রুটি কোডটি পরীক্ষা করুন, যদি এটি অননুমোদিত হিসাবে সমান হয়, OAuth টোকেনটিকে রিফ্রেশ করুন, তারপরে retrofit কলটি পুনরাবৃত্তি করুন। তবে, এর জন্য, সমস্ত কলকে সংশোধন করা উচিত, যা সহজেই রক্ষণাবেক্ষণযোগ্য এবং ভাল সমাধান নয় good সমস্ত retrofit কল পরিবর্তন না করে এটি করার কোনও উপায় আছে কি?


1
এটি আমার অন্যান্য প্রশ্নের সাথে প্রাসঙ্গিক বলে মনে হচ্ছে । আমি শীঘ্রই এটি আবার দেখতে হবে, কিন্তু একটি সম্ভাব্য পদ্ধতির OkHttpClient মোড়ক হয়। এর মতো কিছু: github.com/pakerfeldt/signpost-retrofit এছাড়াও, যেহেতু আমি রবরোস্পাইসটি রিট্রোফিটের সাথে ব্যবহার করছি, তাই বেস অনুরোধের ক্লাস তৈরি করাও অন্য সম্ভাব্য পন্থা হতে পারে। সম্ভবত আপনাকে কোনও প্রসঙ্গ ছাড়াই কীভাবে আপনার প্রবাহ অর্জন করবেন তা নির্ধারণ করতে হবে, সম্ভবত অটো / ইভেন্টবাস ব্যবহার করে।
হাসান ইব্রাহিম

1
আচ্ছা আপনি এটি কাঁটাচামচ করতে পারেন, এবং অপ্রয়োজনীয় কেসগুলি সরাতে পারেন। আমি সম্ভবত এটি আজই সন্ধান করব এবং আমি যদি এমন কিছু অর্জন করি যা আমাদের সমস্যার সমাধান করতে পারে তবে এখানে পোস্ট করব।
ড্যানিয়েল জোলনাই

2
দেখা গেল, গ্রন্থাগারটি সতেজ টোকেনগুলি পরিচালনা করে না, তবে আমাকে একটি ধারণা দিয়েছে। আমি কয়েকটি অচিহ্নিত কোড সম্পর্কে একটি ছোট্ট টুকরোটি তৈরি করেছি, তবে তাত্ত্বিকভাবে, আমি মনে করি এটি কাজ করা উচিত: gist.github.com/ZolnaiDani/9710849
ড্যানিয়েল জোলনাই

3
@ নিউওয়ার্ল্ড এমন একটি সমাধান যা আমি ভাবতে পারি: পরিবর্তনটি টোকইনআরকিউয়েস্ট (...) সিঙ্ক্রোনাইজ করুন এবং প্রথম লাইনে, টোকেনটি রিফ্রেশ করার সময়টি কখন শেষ ছিল তা পরীক্ষা করুন। যদি এটি কয়েক সেকেন্ড আগে (মিলিসেকেন্ড) হয়ে থাকে তবে টোকেনটি রিফ্রেশ করবেন না। টোকেনের বাইরের কোনও সমস্যা পুরানো হওয়ার পরে অবিরত নতুন টোকেনগুলির অনুরোধ করা বন্ধ করতে আপনি এই সময়সীমাটি 1 ঘন্টা বা তার বেশি স্থির করতে পারেন।
ড্যানিয়েল জোলনাই

2
Retrofit 1.9.0 ঠিক OkHttp 2.2 এর জন্য সমর্থন যুক্ত করেছে, এতে ইন্টারসেপ্টর রয়েছে। এটি আপনার কাজটি অনেক সহজ করে তুলবে। আরও তথ্যের জন্য, দেখুন: github.com/square/retrofit/blob/master/… এবং github.com/square/okhttp/wiki/Interceptors আপনাকে এগুলির জন্যও OkHttp প্রসারিত করতে হবে।
ড্যানিয়েল জোলনাই

উত্তর:


213

Interceptorsপ্রমাণীকরণের সাথে ডিল করতে দয়া করে ব্যবহার করবেন না ।

বর্তমানে, প্রমাণীকরণ হ্যান্ডেল করার সর্বোত্তম পন্থাটি হ'ল Authenticatorবিশেষভাবে এই উদ্দেশ্যে ডিজাইন করা নতুন এপিআই ব্যবহার করা ।

OkHttp হবে স্বয়ংক্রিয়ভাবে জিজ্ঞাসা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
@ জড্রুউ দেখে মনে হচ্ছে এই কোডটি 1 বার ব্যর্থ হবে এবং তারপরে এটি অনুরোধ করবে। তবে আপনি যদি কোনও ইন্টারসেপ্টর যোগ করেন তবে কেবল উদ্দেশ্যটি হ'ল সর্বদা অ্যাক্সেস টোকন যুক্ত করা (নির্ধারিত যদি না এর মেয়াদ নির্ধারিত হয় বা না হয়) তবে এটি কেবল তখনই ডাকা হবে যখন 401 পাওয়া যায় যখন কেবলমাত্র সেই টোকেনটির মেয়াদ শেষ হয়ে গেলে।
নার্কিরো

54
TokenAuthenticatorএকটি serviceক্লাস নির্ভর করে । serviceবর্গ একটি উপর নির্ভর করে OkHttpClientদৃষ্টান্ত। একটি তৈরি করতে OkHttpClientআমার দরকার TokenAuthenticator। কীভাবে আমি এই চক্রটি ভেঙে ফেলতে পারি? দুটি ভিন্ন OkHttpClientএস? তাদের বিভিন্ন সংযোগ পুল হতে চলেছে ...
ব্রেইস গাবিন

6
টোকেন রিফ্রেশ করার জন্য কতগুলি সমান্তরাল অনুরোধ রয়েছে? এটি একই সাথে অনেক রিফ্রেশ টোকেন অনুরোধ হবে। কীভাবে এড়ানো যায়?
ইগর কোস্টেনকো

10
ঠিক আছে, সুতরাং @ ইহোর সমস্যার সমাধানটি প্রমাণীকরণকারীর অভ্যন্তরে কোডটি সিঙ্ক্রোনাইজ করা হতে পারে। এটি আমার ক্ষেত্রে সমস্যার সমাধান করেছে। অনুরোধ প্রমাণীকরণের জন্য (...) পদ্ধতি: - যে কোনও ইনিটালাইজেশন স্টাফ করুন - সিঙ্ক্রোনাইজড ব্লক (সিঙ্ক্রোনাইজড (MyAuthenticator.class) start ...}) শুরু করুন - সেই ব্লকে বর্তমান অ্যাক্সেস পুনরুদ্ধার করুন এবং টোকেন রিফ্রেশ করুন - ব্যর্থ অনুরোধটি সর্বশেষ ব্যবহার করছে কিনা তা পরীক্ষা করুন অ্যাক্সেস টোকেন (resp.request ()। শিরোলেখ ("অনুমোদন")) - এটি আপডেট অ্যাক্সেস টোকেন দিয়ে আবার একবার চালনা না করে - অন্যথায় রিফ্রেশ টোকেন প্রবাহ চালান - আপডেট / অবিচ্ছিন্ন অ্যাক্সেস অব্যাহত রাখুন এবং টোকেন - সিঙ্ক্রোনাইজড ব্লক শেষ করুন - পুনরায় চালু করুন
দারিউজ ওয়াইচেকি

65

আপনি ব্যবহার করে থাকেন রেট্রোফিট > = 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 আপনি সঠিক, আপনি ইতিমধ্যে অন্য থ্রেডে থাকা উচিত যখন আপনি মূল অনুরোধ শুরু করেছিলেন যা ইন্টারসেপটরটি চালনার জন্য ট্রিগার করেছিল।
theblang

3
আমার পূর্ববর্তী প্রতিক্রিয়াটি বন্ধ করার বিষয়ে নিশ্চিত হওয়া বা আমি পূর্ববর্তী সংযোগটি ফাঁস করব তা বাদ দিয়ে এটি দুর্দান্ত কাজ করেছে ... চূড়ান্ত অনুরোধ newRequest = विनंती.newBuilder () .... build (); । response.body () ঘনিষ্ঠ (); রিটার্ন চেইন.প্রসেস (নিউ রিকোয়েস্ট);
ডালিনডায়ার

ধন্যবাদ! আমি একটি ইস্যুতে ছুটছিলাম যেখানে মূল অনুরোধের কলব্যাকটি ইন্টারসেপ্টারে শরীর গ্রাস করার কারণে মূল প্রতিক্রিয়ার পরিবর্তে "বন্ধ" এর ত্রুটি বার্তা পেয়েছিল। আমি সফল প্রতিক্রিয়াগুলির জন্য এটি ঠিক করতে সক্ষম হয়েছি, কিন্তু ব্যর্থ প্রতিক্রিয়াগুলির জন্য আমি এটি ঠিক করতে সক্ষম হয়েছি। কোনও পরামর্শ?
lgdroid57

ধন্যবাদ @ ম্যাটব্ল্যাং, সুন্দর লাগছে। একটি প্রশ্ন: অনুরোধ কলব্যাকটি পুনরায় চেষ্টা করার পরেও কল করার নিশ্চয়তা আছে?
লুকা ফাগিওলি

23

টোকেনআউটিক্যান্টিকর একটি পরিষেবা শ্রেণি নির্ভর করে। পরিষেবা শ্রেণি একটি OkHttpClient উদাহরণের উপর নির্ভর করে। একটি OkHttpClient তৈরি করার জন্য আমার টোকেনআউটিক্যান্টিকর দরকার। আমি কীভাবে এই চক্রটি ভাঙ্গতে পারি? দুটি ভিন্ন OkHttpClients? তাদের বিভিন্ন সংযোগ পুল হবে ...

যদি আপনার কাছে থাকে তবে বলুন TokenServiceযে আপনার অভ্যন্তরে প্রয়োজনীয় একটি retrofit 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ক্লাস কোথায় তৈরি হয়?
যোগেশ সুথার

@ যোগেশ সুথার এটি একটি রিট্রোফিট পরিষেবা - সম্পর্কিত প্রশ্নটি দেখুন
ডেভিড

ধন্যবাদ, আপনি কি refreshToken()থেকে বাস্তবায়ন প্রদান করতে পারেন service.refreshToken().execute();? এটি কোথাও বাস্তবায়ন খুঁজে পাচ্ছে না।
যোগেশ সুথার

@ যোগেশ রিফ্রেশ টোকেন পদ্ধতিটি আপনার এপিআই থেকে। আপনি টোকেনকে রিফ্রেশ করতে যাই বলুন না কেন (ব্যবহারকারীর নাম এবং পাসওয়ার্ড সহ একটি কল?)। অথবা একটি অনুরোধ আপনি যেখানে টোকেন জমা দিয়েছেন এবং প্রতিক্রিয়াটি একটি নতুন টোকেন
ডেভিড

5

TokenAuthenticator@ থেবাং উত্তর মত ব্যবহার হ্যান্ডেল করার সঠিক উপায় 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()
    }
}

@ ব্রাইস গ্যাবিন মন্তব্যের মতো নির্ভরতা চক্র প্রতিরোধের জন্য , আমি 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অভ্যন্তরীণ পদ্ধতিটি আবার এ্যাগেইন এবং অ্যাগেইইন বরখাস্তTokenAuthenticator করবে তবে এটি কল পদ্ধতি সম্পর্কে অনেক সময় ত্রুটি ফেলবে ( )। আপনি গণনা প্রতিক্রিয়া দ্বারা এটি প্রতিরোধ করতে পারেন । উদাহরণস্বরূপ, আপনি যদি 3 বার পুনরায় চেষ্টা করার পরে থাকেন তবে শেষ হবে এবংjava.net.ProtocolException: Too many follow-up requestsreturn nullauthenticategetImage()return response 401

  • যদি getImage()প্রতিক্রিয়া সাফল্য => আমরা সাধারণত ফলাফলের হবে (যেমন আপনাকে কল getImage()কোন ত্রুটি সহ)

আশা করি এটি সাহায্য করবে


এই সমাধানটি আপনার সার্ভিস জেনারেটর শ্রেণিতে স্পষ্ট হিসাবে 2 টি পৃথক OkHttpClients ব্যবহার করে।
স্পেশালসনফ্লেক

পছন্দ করুন আপনি যদি আমার সমাধানটি অনুসরণ করেন তবে আপনাকে 2 ওকেএইচটিপি তৈরি করতে হবে কারণ আমি 2 টি পরিষেবা তৈরি করেছি (ওআউথ এবং কোনওটিই নয়)। আমি মনে করি এটি কোনও সমস্যার কারণ হবে না। আমাকে আপনার ধারণাটি জানতে দিন
ফান ভ্যান লিনহ

1

আমি এটি একটি পুরানো থ্রেড জানি, কিন্তু ঠিক যদি কেউ এতে হোঁচট খায়।

টোকেনআউটিক্যান্টিকর একটি পরিষেবা শ্রেণি নির্ভর করে। পরিষেবা শ্রেণি একটি OkHttpClient উদাহরণের উপর নির্ভর করে। একটি OkHttpClient তৈরি করার জন্য আমার টোকেনআউটিক্যান্টিকর দরকার। আমি কীভাবে এই চক্রটি ভাঙ্গতে পারি? দুটি ভিন্ন OkHttpClients? তাদের বিভিন্ন সংযোগ পুল হবে ...

আমিও একই সমস্যার মুখোমুখি হয়েছি, তবে আমি কেবলমাত্র টোকেনআউটিক্যান্টের জন্য নিজেই অন্য একটি প্রয়োজন বলে মনে করি না যে আমি নিজেই ডাগার 2 ব্যবহার করছিলাম, সুতরাং আমি অলস ইনজেকশন হিসাবে পরিষেবা ক্লাস সরবরাহ শেষ করেছি । টোকেনআউটিক্যান্টিকর, আপনি এখানে খঞ্জের ২ তে অলস ইনজেকশন সম্পর্কে আরও পড়তে পারেন , তবে এটি মূলত ডাগারকে বলে নয় যান এবং সেবা TokenAuthenticator প্রয়োজনীয় সরাসরি তৈরি করুন।

আপনি নমুনা কোডের জন্য এই এসও থ্রেডটি উল্লেখ করতে পারেন: ডাগার 2 ব্যবহার করার সময় একটি বিজ্ঞপ্তি নির্ভরতা কীভাবে সমাধান করবেন?


0

আপনি আপনার সমস্ত লোডারদের জন্য একটি বেস ক্লাস তৈরি করার চেষ্টা করতে পারেন যাতে আপনি একটি বিশেষ ব্যতিক্রম ধরতে সক্ষম হন এবং তারপরে আপনার প্রয়োজন অনুযায়ী কাজ করতে পারেন। আচরণটি ছড়িয়ে দেওয়ার জন্য আপনার সমস্ত পৃথক লোডারগুলি বেস বর্গ থেকে প্রসারিত করুন।


রিট্রোফিট সেভাবে কাজ করে না। এটি একটি এপিআই কল বর্ণনা করতে জাভা টীকাগুলি এবং ইন্টারফেসগুলি ব্যবহার করে
ড্যানিয়েল জোলনাই

আমি জানি যে কীভাবে retrofit কাজ করে, তবে আপনি এখনও একটি AsynTask এর মধ্যে আপনার API কলগুলি "মোড়ানো" আছেন?
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;
    }
}

এমন কোনও কারণ আছে যা আপনি প্রস্তাবিত সমাধানের পরিবর্তে অ্যাপাচিচিয়েন্ট ব্যবহার করছেন? এটি কোনও ভাল সমাধান নয় তা নয়, তবে ইন্টারসেপ্টর ব্যবহারের তুলনায় এটি আরও অনেক বেশি কোডিং প্রয়োজন।
ড্যানিয়েল জোলনাই

এটি কুকি ধ্রুবক ক্লায়েন্ট হিসাবে কাস্টমাইজড, পরিষেবা জুড়ে সেশন বজায় রাখে। এমনকি অনুরোধ ইন্টারসিসেপ্টারে আপনি হেডারে অ্যাকসেসটোকেন যুক্ত করতে পারেন। তবে আপনি যদি এটি পরম হিসাবে যুক্ত করতে চান? এছাড়াও OkHTTPClient এর সীমাবদ্ধতা রয়েছে। রেফ: স্ট্যাকওভারফ্লো.com
প্রশ্নগুলি

এটি যেকোন ক্ষেত্রে ব্যবহার করার জন্য আরও সাধারণীকরণ করা হয় 1. কুকি পার্সেন্টেন্ট ক্লায়েন্ট 2. এইচটিটিপি এবং এইচটিটিপিএস অনুরোধ গ্রহণ করে 3. প্যারামে অ্যাক্সেস টোকন আপডেট করুন।
সুনীল প্রকাশ

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
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.