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

前言

今年是9102年了,應該沒有還在用userId來鑒權了吧,也應該很少人使用cookie來保持會話了吧?而現在更常用的是Authorization,

關于Authorization

簡略的講一講Authorization,如果要深入了解的話請看底部的參考文章鏈接。Authorization的認證方式在我接觸中有兩種

  • Basic
  • Bearer

Basic

HTTP基本認證,在請求的時候加上以下請求頭:

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

將用戶名和密碼用英文冒號(:)拼接起來,并進行一次Base64編碼。服務端拿到basic碼,然后自己查詢相關信息再按照base64encode(username+":"+password))的方式得出當前用戶的basic進行對比。

Bearer

授權完成后會返回類似下面的數據結構:

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

而其中的refresh_token的作用是在access_token失效的時候進行重新刷新傳入的參數,具體怎么傳要看各自項目的實現方式。
access_token就是我們的認證令牌。token_type是令牌的類型,而我現在使用到的只有bearer,其它類型未碰到,希望各位看官能補充一下。
在使用的時候需要加上以下請求頭:

Authorization : token_type access_token

也就是這樣:

Authorization: Bearer xxxxx

實現

方式1 :authenticator

authenticator是在創建OkHttpClient的時候能夠設置的一個方法,接收的是一個okhttp3.Authenticator的interface,默認不設置的話是一個NONE的空實現,而回調的地方是在okhttp3.internal.http.RetryAndFollowUpInterceptor.followUpRequest()

Authenticator
followUpRequest

編碼

相關代碼也比較簡單,在okhttp3.Authenticator的注釋上面也寫有簡單的例子,核心代碼就以下幾行:

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

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

以上就是主要代碼,其中演示的是basic方式的認證模式,bearer方式的沒實現,其實也只是refreshToken()中發起一個同步請求去刷新一下token并保存,后面的步驟都是一樣的。

如何使用

創建OkHttpClient調用,當然,也可以直接寫匿名內部類的實現,都是可以的。

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

演示

為了演示方便看到結果,我在Authenticator的實現類中增加了一些回調主線程的方法,具體看一下源碼即可,對于主要結果沒什么影響。

Authenticator

總結

使用官方提供的Authenticator有一個很明顯的問題,那就是會占用重試,像示例中,我并沒有傳入一個正確的token,就導致一直在回調Authenticator,直到達到了最大重試次數為止。而往往需求是token失效以后選擇重試一次,成功了繼續請求,再次失敗則提示登錄,所以這個方法使用得不多。

方式2 :Interceptor

上面okhttp3.Authenticator的實現方式其實是在RetryAndFollowUpInterceptor中判斷和回調的,由此,可以自定義一個Interceptor,由開發者來自行判斷和跳轉。

編碼

詳細代碼如下:

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

            // 再次發起請求
            return chain.proceed(retryRequest);
        }

        return response;
    }
}

使用

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

演示

AuthenticatorInterceptor

總結

從演示中可以看出,在第一次返回401的時候,進行了一次token的獲取,并且再次進行了請求,圓滿符合我們的預期,只重試一次。

最后

分析

可能會有疑問:為什么使用Interceptor就能達到我們預期的效果?Interceptor到底是如何工作的?

首先Interceptor添加是有先后順序的,首先添加的是我們設置的Interceptor,然后添加的才是okhttpInterceptor。如源碼中:

Add Interceptor

總的來說,okhttp的實現方式就是通過Interceptor來組成一個一個的chian來實現的。每個Interceptor里面的intercept()方法內部都會調用Chain.proceed()方法,將請求交給下一個Interceptor,由此類推,一直到最后一個Interceptor請求完成。
需要注意的是proceed是同步的,也就是調用proceed方法之后需要等等下一個Interceptor進行處理,當最后一個Interceptor請求到數據,經過自己的處理之后,再往上返回Response,直到第一個Interceptor為止,返回數據。主要關系如下圖:

Interceptor ex

這些所有的Interceptor里面的proceed都是調用了一次,那么我們增加一個Interceptor,等到proceed返回了Response之后,對Response進行判斷,如果是認證失敗,我們則刷新一下token,重新創建Request,再調用一次proceed方法。如果再失敗了,就不會再回調到當前的Interceptor,如下圖:

AuthenticatorInterceptor

源碼

參考文章

微信公眾號

AndroidRookie
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容