RxJava2 + Retrofit2 完全指南 之 Authenticator處理與Token靜默刷新

前言

今年是9102年了,應(yīng)該沒(méi)有還在用userId來(lái)鑒權(quán)了吧,也應(yīng)該很少人使用cookie來(lái)保持會(huì)話了吧?而現(xiàn)在更常用的是Authorization,

關(guān)于Authorization

簡(jiǎn)略的講一講Authorization,如果要深入了解的話請(qǐng)看底部的參考文章鏈接。Authorization的認(rèn)證方式在我接觸中有兩種

  • Basic
  • Bearer

Basic

HTTP基本認(rèn)證,在請(qǐng)求的時(shí)候加上以下請(qǐng)求頭:

Authorization : basic base64encode(username+":"+password))

將用戶名和密碼用英文冒號(hào)(:)拼接起來(lái),并進(jìn)行一次Base64編碼。服務(wù)端拿到basic碼,然后自己查詢相關(guān)信息再按照base64encode(username+":"+password))的方式得出當(dāng)前用戶的basic進(jìn)行對(duì)比。

Bearer

授權(quán)完成后會(huì)返回類(lèi)似下面的數(shù)據(jù)結(jié)構(gòu):

{
    "token_type": "Bearer",
    "access_token": "xxxxx",
    "refresh_token": "xxxxx"
}

而其中的refresh_token的作用是在access_token失效的時(shí)候進(jìn)行重新刷新傳入的參數(shù),具體怎么傳要看各自項(xiàng)目的實(shí)現(xiàn)方式。
access_token就是我們的認(rèn)證令牌。token_type是令牌的類(lèi)型,而我現(xiàn)在使用到的只有bearer,其它類(lèi)型未碰到,希望各位看官能補(bǔ)充一下。
在使用的時(shí)候需要加上以下請(qǐng)求頭:

Authorization : token_type access_token

也就是這樣:

Authorization: Bearer xxxxx

實(shí)現(xiàn)

方式1 :authenticator

authenticator是在創(chuàng)建OkHttpClient的時(shí)候能夠設(shè)置的一個(gè)方法,接收的是一個(gè)okhttp3.Authenticator的interface,默認(rèn)不設(shè)置的話是一個(gè)NONE的空實(shí)現(xiàn),而回調(diào)的地方是在okhttp3.internal.http.RetryAndFollowUpInterceptor.followUpRequest()

Authenticator
followUpRequest

編碼

相關(guān)代碼也比較簡(jiǎn)單,在okhttp3.Authenticator的注釋上面也寫(xiě)有簡(jiǎn)單的例子,核心代碼就以下幾行:

private Authenticator authorization = new Authenticator() {
        @Override
        public Request authenticate(Route route, Response response) throws IOException {

            //-----------核心代碼-------
            // 這里拋出的錯(cuò)誤會(huì)直接回調(diào) onError
            // 這里發(fā)起的請(qǐng)求是同步的,刷新完成token后再增加到header中
            // String token = refreshToken();
            String token = Credentials.basic("userName", "password", Charset.forName("UTF-8"));
            return response.request()
                    .newBuilder()
                    .header("Authorization", token)
                    .build();
            //-----------核心代碼-------
        }
    };

以上就是主要代碼,其中演示的是basic方式的認(rèn)證模式,bearer方式的沒(méi)實(shí)現(xiàn),其實(shí)也只是refreshToken()中發(fā)起一個(gè)同步請(qǐng)求去刷新一下token并保存,后面的步驟都是一樣的。

如何使用

創(chuàng)建OkHttpClient調(diào)用,當(dāng)然,也可以直接寫(xiě)匿名內(nèi)部類(lèi)的實(shí)現(xiàn),都是可以的。

retrofit = new Retrofit.Builder()
                .client(new OkHttpClient.Builder()
                        .authenticator(authorization)// 增加重試
                        .addInterceptor(getHttpLoggingInterceptor())
                        .build())
                .baseUrl("https://api.github.com/")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

演示

為了演示方便看到結(jié)果,我在Authenticator的實(shí)現(xiàn)類(lèi)中增加了一些回調(diào)主線程的方法,具體看一下源碼即可,對(duì)于主要結(jié)果沒(méi)什么影響。

Authenticator

總結(jié)

使用官方提供的Authenticator有一個(gè)很明顯的問(wèn)題,那就是會(huì)占用重試,像示例中,我并沒(méi)有傳入一個(gè)正確的token,就導(dǎo)致一直在回調(diào)Authenticator,直到達(dá)到了最大重試次數(shù)為止。而往往需求是token失效以后選擇重試一次,成功了繼續(xù)請(qǐng)求,再次失敗則提示登錄,所以這個(gè)方法使用得不多。

方式2 :Interceptor

上面okhttp3.Authenticator的實(shí)現(xiàn)方式其實(shí)是在RetryAndFollowUpInterceptor中判斷和回調(diào)的,由此,可以自定義一個(gè)Interceptor,由開(kāi)發(fā)者來(lái)自行判斷和跳轉(zhuǎn)。

編碼

詳細(xì)代碼如下:

Interceptor mAuthenticatorInterceptor = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        // 獲取請(qǐng)求
        Request request = chain.request();
        // 獲取響應(yīng)
        Response response = chain.proceed(request);
        // 在這里判斷是不是是token失效
        // 當(dāng)然,判斷條件不會(huì)這么簡(jiǎn)單,會(huì)有更多的判斷條件的
        if (response.code() == 401) {
            // 這里應(yīng)該調(diào)用自己的刷新token的接口
            // 這里發(fā)起的請(qǐng)求是同步的,刷新完成token后再增加到header中
            // 這里拋出的錯(cuò)誤會(huì)直接回調(diào) onError
//                String token = refreshToken();
            String token = Credentials.basic("userName", "password", Charset.forName("UTF-8"));
            // 創(chuàng)建新的請(qǐng)求,并增加header
            Request retryRequest = chain.request()
                    .newBuilder()
                    .header("Authorization", token)
                    .build();

            // 再次發(fā)起請(qǐng)求
            return chain.proceed(retryRequest);
        }

        return response;
    }
}

使用

和方法一相同,在創(chuàng)建HttpClient的時(shí)候addInterceptor(mAuthenticatorInterceptor),將我們自己的攔截器加入進(jìn)行即可。

演示

AuthenticatorInterceptor

總結(jié)

從演示中可以看出,在第一次返回401的時(shí)候,進(jìn)行了一次token的獲取,并且再次進(jìn)行了請(qǐng)求,圓滿符合我們的預(yù)期,只重試一次。

最后

分析

可能會(huì)有疑問(wèn):為什么使用Interceptor就能達(dá)到我們預(yù)期的效果?Interceptor到底是如何工作的?

首先Interceptor添加是有先后順序的,首先添加的是我們?cè)O(shè)置的Interceptor,然后添加的才是okhttpInterceptor。如源碼中:

Add Interceptor

總的來(lái)說(shuō),okhttp的實(shí)現(xiàn)方式就是通過(guò)Interceptor來(lái)組成一個(gè)一個(gè)的chian來(lái)實(shí)現(xiàn)的。每個(gè)Interceptor里面的intercept()方法內(nèi)部都會(huì)調(diào)用Chain.proceed()方法,將請(qǐng)求交給下一個(gè)Interceptor,由此類(lèi)推,一直到最后一個(gè)Interceptor請(qǐng)求完成。
需要注意的是proceed是同步的,也就是調(diào)用proceed方法之后需要等等下一個(gè)Interceptor進(jìn)行處理,當(dāng)最后一個(gè)Interceptor請(qǐng)求到數(shù)據(jù),經(jīng)過(guò)自己的處理之后,再往上返回Response,直到第一個(gè)Interceptor為止,返回?cái)?shù)據(jù)。主要關(guān)系如下圖:

Interceptor ex

這些所有的Interceptor里面的proceed都是調(diào)用了一次,那么我們?cè)黾右粋€(gè)Interceptor,等到proceed返回了Response之后,對(duì)Response進(jìn)行判斷,如果是認(rèn)證失敗,我們則刷新一下token,重新創(chuàng)建Request,再調(diào)用一次proceed方法。如果再失敗了,就不會(huì)再回調(diào)到當(dāng)前的Interceptor,如下圖:

AuthenticatorInterceptor

源碼

參考文章

微信公眾號(hào)

AndroidRookie
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容