Tüm aramaları değiştirmeden Retrofit kullanarak OAuth jetonunu yenileme


157

Retrofit'i, OAuth2 güvenli sunucusuyla iletişim kurmak için Android uygulamamızda kullanıyoruz. Her şey harika çalışıyor, her çağrıya erişim belirtecini eklemek için RequestInterceptor'u kullanıyoruz. Ancak, erişim belirtecinin süresinin dolduğu ve belirtecin yenilenmesi gereken zamanlar olacaktır. Simgenin süresi dolduğunda, bir sonraki çağrı Yetkisiz bir HTTP koduyla geri döner, bu nedenle izlenmesi kolaydır. Her Retrofit çağrısını şu şekilde değiştirebiliriz: Hata geri çağrısında hata kodunu kontrol edin, Yetkisiz olarak eşitlerse, OAuth jetonunu yenileyin ve ardından Retrofit çağrısını tekrarlayın. Ancak, bunun için, tüm çağrılar kolayca değiştirilebilen ve iyi bir çözüm olmayan değiştirilmelidir. Tüm Retrofit çağrılarını değiştirmeden bunu yapmanın bir yolu var mı?


1
Bu benim diğer sorumla alakalı görünüyor . Yakında tekrar inceleyeceğim, ancak olası bir yaklaşım OkHttpClient'i sarmaktır. Bunun gibi bir şey: github.com/pakerfeldt/signpost-retrofit Ayrıca, Retrofit ile RoboSpice kullandığım için, bir temel İstek sınıfı oluşturmak da başka bir olası yaklaşım olabilir. Muhtemelen bir Bağlam olmadan akışınıza nasıl ulaşacağınızı, belki Otto / EventBus'u kullanarak çözmeniz gerekecek.
Hassan Ibraheem

1
Eh, çatallayabilir ve gereksiz vakaları kaldırabilirsiniz. Bugün bu konuya bakacağım ve sorunumuzu çözebilecek bir şey başarırsam buraya göndereceğim.
Daniel Zolnai

2
Anlaşıldı ki, kütüphane canlandırıcı jetonlarla uğraşmadı, ama bana bir fikir verdi. Bazı denenmemiş kodlar hakkında küçük bir yumruk
Daniel Zolnai

3
@neworld Düşünebileceğim bir çözüm: changeTokenInRequest (...) eşitlemesini yapın ve ilk satırda belirtecin en son ne zaman yenilendiğini kontrol edin. Sadece birkaç saniye (milisaniye) önce gelmişse, jetonu yenilemeyin. Ayrıca, jetonun modası geçmiş başka bir sorunu olduğunda sürekli olarak yeni jeton istemeyi durdurmak için bu zaman aralığını 1 saate kadar ayarlayabilirsiniz.
Daniel Zolnai

2
Retrofit 1.9.0, durduruculara sahip OkHttp 2.2 için destek ekledi. Bu işinizi çok daha kolay hale getirmelidir. Daha fazla bilgi için, bkz. Github.com/square/retrofit/blob/master/… ve github.com/square/okhttp/wiki/Interceptors Bununla birlikte , OkHttp'yi de genişletmeniz gerekir.
Daniel Zolnai

Yanıtlar:


214

Lütfen Interceptorskimlik doğrulamasıyla uğraşmak için kullanmayın .

Şu anda, kimlik doğrulamasını ele almak için en iyi yaklaşım Authenticator, özellikle bu amaç için tasarlanmış yeni API'yi kullanmaktır .

OkHttp edecek otomatik sormakAuthenticator bir yanıt olduğunda kimlik bilgileri için 401 Not Authorised son başarısız isteği Yeniden deniyor onlarla.

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

Bir takın Authenticatorbir karşı OkHttpClientsizinle aynı şeyleriInterceptors

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

Bu istemciyi oluştururken kullanın Retrofit RestAdapter

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

6
Bu, her isteğin her zaman 1 kez başarısız olacağı anlamına mı geliyor yoksa istek yaparken jeton ekliyor musunuz?
Jdruwe

11
@Jdruwe, bu kod 1 kez başarısız olacak gibi görünüyor ve sonra isteği yapacak. ancak tek amaç olan bir durdurucu eklerseniz, her zaman bir erişim belirteci eklemektir (süresi dolup dolmadığına bakılmaksızın), ancak bu yalnızca yalnızca belirtecin süresi dolduğunda gerçekleşecek olan bir 401 alındığında çağrılır.
narciero

54
TokenAuthenticatorbir servicesınıfa bağlıdır . serviceSınıf bir bağlıdır OkHttpClientörneği. Bir oluşturmak için OkHttpClientihtiyacım var TokenAuthenticator. Bu döngüyü nasıl kırabilirim? İki farklı OkHttpClients? Farklı bağlantı havuzları olacak ...
Brais Gabin

6
Jetonu yenilemesi gereken birçok paralel talebe ne dersiniz? Aynı anda birçok yenileme jetonu isteği olacaktır. Nasıl önlenir?
Igor Kostenko

10
Tamam, bu yüzden @ Ihor sorunun çözümü Authenticator içindeki kodu senkronize ediyor olabilir. Benim durumumdaki sorunu çözdü. in Authenticate iste (...) yöntemi: - herhangi bir inisalizasyon işi yap - senkronize bloğu başlat (senkronize (MyAuthenticator.class) {...}) - bu blokta geçerli erişim ve yenileme jetonunu al - başarısız isteğin en son kullanılıp kullanılmadığını kontrol et erişim belirteci (resp.request (). üstbilgi ("Yetkilendirme")) - yalnızca güncellenmiş erişim belirteciyle bir kez daha çalıştırılmazsa - aksi takdirde yenileme belirteci akışını güncelle - güncelleştirme / kalıcı erişim ve yenileme belirtecini bitir - eşitlenmiş bloğu bitir - yeniden çalıştır
Dariusz Wiechecki

65

Eğer kullanıyorsanız Sonradan montaj > = 1.9.0o zaman devreye sokabilir OkHttp en yeni keseni tanıtıldı, OkHttp 2.2.0. Size izin veren bir Uygulama Durdurucu kullanmak istersiniz retry and make multiple calls.

Interceptor'unuz şu sahte kod gibi bir şeye benzeyebilir:

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

}

Kimliğinizi tanımladıktan sonra Interceptor, bir OkHttpClientdurdurucu oluşturun ve bir Uygulama Durdurucu olarak ekleyin .

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

Ve son olarak, bunu OkHttpClientoluştururken kullanın RestAdapter.

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

Uyarı: Farklı Jesse Wilson(Kare itibaren) bahseder burada , bu gücün tehlikeli bir miktardır.

Bununla birlikte, kesinlikle böyle bir şeyi şimdi ele almanın en iyi yolu olduğunu düşünüyorum. Herhangi bir sorunuz varsa lütfen yorum sormaktan çekinmeyin.


2
Android ana iş parçacığında ağ çağrılarına izin vermediğinde Android'de nasıl eşzamanlı arama gerçekleştiriyorsunuz? Eşzamansız bir çağrının yanıtı döndüren sorunlarla karşılaşıyorum.
lgdroid57

1
@ lgdroid57 Haklısınız, bu nedenle önleyicinin çalışmasını tetikleyen orijinal isteği başlattığınızda başka bir iş parçacığında olmalısınız.
theblang

3
Bu önceki yanıtı kapatmak emin olmak zorunda kaldı ya da önceki bağlantı kaçak olurdu dışında harika çalıştı ... son İstek newRequest = request.newBuilder () .... build (); . response.body () yakın (); dönüş zinciri. işlem (newRequest);
DallinDyer

Teşekkürler! Interceptor'da tüketildiği için, orijinal isteğin Geri Çağrısının orijinal yanıt yerine "kapalı" hata mesajı aldığı bir sorunla karşılaşıyordum. Başarılı yanıtlar için bunu düzeltebildim, ancak başarısız yanıtlar için düzeltemedim. Baska öneri?
lgdroid57

Teşekkürler @mattblang, güzel görünüyor. Bir soru: Tekrar denemede bile geri arama isteği garanti ediliyor mu?
Luca Fagioli

23

TokenAuthenticator bir hizmet sınıfına bağlıdır. Hizmet sınıfı bir OkHttpClient örneğine bağlıdır. Bir OkHttpClient oluşturmak için TokenAuthenticator'a ihtiyacım var. Bu döngüyü nasıl kırabilirim? İki farklı OkHttpClients? Farklı bağlantı havuzları olacak.

Diyelim ki, TokenServiceiçinde ihtiyacınız olan bir Güçlendirme varsa, Authenticatorancak sadece bir tane ayarlamak OkHttpClientistiyorsanız, TokenServiceHolderbir bağımlılık olarak kullanabilirsiniz .TokenAuthenticator . Uygulama (singleton) düzeyinde bir referans bulundurmanız gerekir. Dagger 2 kullanıyorsanız bu kolaydır, aksi takdirde Uygulamanız içinde sınıf alanı oluşturun.

İçinde 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;
    }

İçinde TokenServiceHolder.java:

public class TokenServiceHolder {

    TokenService tokenService = null;

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

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

İstemci kurulumu:

//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);

Dagger 2 veya benzer bir bağımlılık enjeksiyon çerçevesi kullanıyorsanız, bu sorunun cevaplarında bazı örnekler vardır


TokenServiceSınıf nerede oluşturulur?
Yogesh Suthar

@YogeshSuthar Bu bir Retrofit hizmetidir - ilgili soruya bakın
David Rawson

Sayesinde, uygulanmasını sağlayabilir refreshToken()dan service.refreshToken().execute();. Uygulamasını hiçbir yerde bulamıyor.
Yogesh Suthar

@Yogesh refreshToken yöntemi API'nizden alınmıştır. Bir jetonu yenilemek için ne ararsanız (kullanıcı adı ve şifreyle bir çağrı olabilir mi?). Ya da belki bir jeton gönderdiğiniz ve yanıtın yeni bir jeton olduğu bir talep
David Rawson

5

TokenAuthenticatorLike @theblang yanıtı kullanmak işlemek için doğru bir yoldur refresh_token.

İşte benim alet (Kotlin, Dagger, RX kullanıyorum ama bu fikri davanıza uygulamak için kullanabilirsiniz)
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()
    }
}

Öneleyen için bağımlılık döngüsü @Brais Gabin açıklama gibi, ben oluşturmak 2 arayüzü gibi

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

ve

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

AccessTokenWrapper sınıf

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 sınıf

data class AccessToken(
        @Expose
        var token: String,

        @Expose
        var refreshToken: String)

Önleme Sahibim

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

Son olarak, eklemek Interceptorve Authenticatorsizin için OKHttpClientoluşturabilir servis PotoAuthApi

gösteri

https://github.com/PhanVanLinh/AndroidMVPKotlin

Not

Kimlik doğrulayıcı akışı
  • Örnek API getImage()dönüşü 401 hata kodu
  • authenticateyöntem içeride TokenAuthenticatorolacak ateş
  • Senkronize noneAuthAPI.refreshToken(...)denilen
  • Yanıttan sonra noneAuthAPI.refreshToken(...)-> yeni simge başlığa eklenecek
  • getImage()irade OTO denilen (yeni bir başlıkla HttpLogging giriş OLMAYACAKTIR (bu çağrıyı) interceptiçindeki AuthInterceptor WILL DİYE DEĞİL )
  • Eğer getImage()hala hata 401 ile başarısız oldu, authenticateiçeride yöntem TokenAuthenticatorirade TEKRAR ateş ve TEKRAR (o zaman çağrı yöntemi birçok zaman hakkında hata atmak olacaktır java.net.ProtocolException: Too many follow-up requests). Cevabı sayarak önleyebilirsiniz . Örnek, eğer return nulliçinde authenticate3 kez yeniden deneme sonrasında, getImage()olacak bitirmek vereturn response 401

  • Eğer getImage()(aramak gibi tepki başarı => normalde sonucu sonuçlanacaktır getImage()hiçbir hata ile)

Umarım yardımcı olur


Bu çözüm, ServiceGenerator sınıfınızda görüldüğü gibi 2 farklı OkHttpClients kullanır.
SpecialSnowflake

@SpecialSnowflake haklısın. Çözümümü takip ederseniz, 2 hizmet (oauth ve none auth) oluşturduğum için 2 OkHttp oluşturmanız gerekir. Bence herhangi bir soruna neden olmayacak. Fikrinizi bildirin
Phan Van Linh

1

Bu eski bir iş parçacığını biliyorum, ama birisinin tökezlemesi durumunda.

TokenAuthenticator bir hizmet sınıfına bağlıdır. Hizmet sınıfı bir OkHttpClient örneğine bağlıdır. Bir OkHttpClient oluşturmak için TokenAuthenticator'a ihtiyacım var. Bu döngüyü nasıl kırabilirim? İki farklı OkHttpClients? Farklı bağlantı havuzları olacak.

Ben de aynı sorunla karşı karşıya idi, ama ben hizmet sınıfını sağlayan sona erdi yüzden, ben, ben Dagger2 kullanıyordum sadece TokenAuthenticator kendisi için başka bir ihtiyacı olduğunu sanmıyorum çünkü sadece bir OkHttpClient yaratmak istedik Tembel enjekte içinde TokenAuthenticator, sen hançer 2'de Tembel enjeksiyonu hakkında daha fazla bilgi bulabilirsiniz burada , ama temelde için hançermiş için demek gibi dEĞİL hemen gidip TokenAuthenticator ihtiyaç duyduğu hizmeti oluşturun.

Örnek kod için bu SO iş parçacığına başvurabilirsiniz: Dagger2'yi kullanırken dairesel bir bağımlılık nasıl çözülür?


0

Belirli bir istisnayı yakalayabileceğiniz ve daha sonra istediğiniz gibi davranabileceğiniz tüm yükleyicileriniz için bir temel sınıf oluşturmayı deneyebilirsiniz. Davranışı yaymak için tüm farklı yükleyicilerinizin temel sınıftan genişlemesini sağlayın.


Retrofit bu şekilde çalışmaz. Bir API çağrısını tanımlamak için java ek açıklamaları ve arayüzler kullanır
Daniel Zolnai

Sonradan güçlendirme işleminin nasıl çalıştığını biliyorum, ancak API çağrılarınızı bir AsynTask'ın içine "sarıyorsunuz" değil mi?
k3v1n4ud3

Hayır, çağrıları geri arama ile kullanıyorum, bu yüzden zaman uyumsuz olarak çalışıyorlar.
Daniel Zolnai

Daha sonra muhtemelen bir temel geri arama sınıfı oluşturabilir ve tüm geri aramalarınızın onu genişletmesini sağlayabilirsiniz.
k3v1n4ud3

2
Bunun bir çözümü var mı? Tam burada benim durumum. = /
Hugo Nogueira

0

Uzun araştırmalardan sonra, Apache istemcisini, erişim belirtecini parametre olarak gönderdiğiniz Retrofit İçin Yenileme AccessToken'i işlemek için özelleştirdim.

Adaptörünüzü çerez Kalıcı İstemci ile başlatın

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

Yetkisiz erişim ERROR_CODE = 401 ise, erişim belirtecini yenileyin ve isteği hatırlayın, tüm isteklerin çerezlerini ve her istek yanıtını kontrol eden Çerez Kalıcı istemci, yalnızca isteği işler.

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

Önerilen çözüm yerine ApacheClient kullanmanızın bir nedeni var mı? İyi bir çözüm olmadığı için değil, Interceptors kullanmaya kıyasla çok daha fazla kodlamaya ihtiyacı var.
Daniel Zolnai

Çerez kalıcı istemci olarak özelleştirilmiş, hizmetler boyunca oturumu korur. İstek Durdurucu'da bile, başlıklara erişim belirteci ekleyebilirsiniz. Ama ya bir param olarak eklemek isterseniz? Ayrıca OKHTTPClient'in de sınırları vardır. ref: stackoverflow.com/questions/24594823/…
Suneel Prakash

Her durumda kullanılması daha genelleştirilmiştir 1. Çerez Kalıcı İstemci 2. HTTP ve HTTPS isteklerini kabul eder 3. Erişim Simgesini Params olarak güncelleyin.
Suneel Prakash

0

İşi bir Durdurucu (belirteci enjekte edin) ve bir Kimlik Doğrulayıcı (yenileme işlemleri) kullanarak yapın:

Ben de bir çift çağrı sorunu vardı: ilk çağrı her zaman bir 401 döndürdü : jeton ilk çağrı (enjektör) enjekte değildi ve kimlik doğrulayıcı çağrıldı: iki istek yapıldı.

Düzeltme sadece Interceptor'daki yapıya isteği yeniden reddetmekti:

ÖNCE:

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

SONRA:

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

BİR BLOKTA:

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

Umarım yardımcı olur.

Düzenleme: Ben her zaman sadece kimlik doğrulayıcı ve hiçbir önleme kullanarak 401 dönen ilk çağrı önlemek için bir yol bulamadık


-2

Simgeyi yenilerken eşzamanlı / paralel çağrıları çözmek isteyen herkese. İşte bir geçici çözüm

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
    }
}
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.