談?wù)勗赗etrofit2中自定義攔截器的經(jīng)驗(yàn)

現(xiàn)在大部分APP都存在登錄,為了驗(yàn)證接口的安全性,都會(huì)在登錄成功后返回一個(gè)token,或者其他方式的驗(yàn)證方式.接下來(lái)講的都是在項(xiàng)目中遇到的坑以及處理
項(xiàng)目中,登錄成功后會(huì)返回返回給我?guī)讉€(gè)字段,一個(gè)是access_token,一個(gè)refresh_token,一個(gè)access_token到期時(shí)間
access_token一般是在網(wǎng)絡(luò)請(qǐng)求的是否添加到Header的,如下

HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.level(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .retryOnConnectionFailure(true)
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .addInterceptor(chain -> {
                    Request request = chain.request();
                    Request build = request.newBuilder()
                            .addHeader("Authorization", "Bearer "+newToken)
                            .build();
                    return chain.proceed(build);
                });

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(builder.build())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        return retrofit.create(t);
Request build = request.newBuilder()
                            .addHeader("Authorization", "Bearer "+newToken)
                            .build();

這里的addHeader()就是用來(lái)將token添加到頭部用的
Bearer這是token的一種類型,還有一種Basic,至于用法,還未去深入了解過(guò),代碼中是套用的之前接手回來(lái)的項(xiàng)目
今天的重點(diǎn)就是addInterceptor,上面代碼中是一個(gè)常用的使用,添加一個(gè)基本的攔截器,由于項(xiàng)目中需要處理access_token的過(guò)期處理,又不想緩存大量跟access_token相關(guān)的內(nèi)容,在網(wǎng)上參考了許多攔截器相關(guān)的代碼,由于對(duì)http相關(guān)的知識(shí)了解的并不多,期間也踩了許多坑.要自定義攔截器,好的方式就是創(chuàng)建一個(gè)繼承Interceptor的類TokenInterceptor,代碼使用的kotlin
先上我寫(xiě)的主要部分

override fun intercept(chain: Interceptor.Chain): Response {
        //當(dāng)前時(shí)間
        val nowTime = System.currentTimeMillis()
        //登錄成功時(shí)的時(shí)間-當(dāng)前時(shí)間得到時(shí)間差
        val timeDifference = SPUtils.getLong(App.getInstance().baseContext, TOKEN_TIME, TOKEN_TIME, 0) - nowTime
        if ((App.getInstance().access_token.isEmpty()) or (timeDifference > 86399)) {
            //如果access_token為空,或者 access_token有效期超過(guò)24小時(shí),需要刷新token
            //獲取新的token
            val newToken = getNewToken()
            //重置access_token保存的時(shí)間
            SPUtils.setLong(App.getInstance().baseContext, TOKEN_TIME, TOKEN_TIME, System.currentTimeMillis())
            Log.e("newToken:", newToken)
            App.getInstance().access_token = newToken
            val newRequest = chain.request()
                    .newBuilder()
                    .addHeader("Authorization", "Bearer $newToken")
                    .build()
            return chain.proceed(newRequest)
        } else {
            val oldRequest = chain.request()
                    .newBuilder()
                    .addHeader("Authorization", "Bearer $token")
                    .build()
            return chain.proceed(oldRequest)
        }
    }

因?yàn)槲以诘卿洺晒r(shí)保存了當(dāng)時(shí)的時(shí)間,也就是當(dāng)時(shí)的System.currentTimeMillis()
,所以在成功刷新token后需要重置時(shí)間,我這里的判斷其實(shí)并不友好,對(duì)于和后端約定的token過(guò)期的code并沒(méi)有處理,不過(guò)思路是這樣的
判斷請(qǐng)求接口時(shí)的時(shí)間差是否大于等于24小時(shí),如果是,就需要去刷新token,如果沒(méi)有過(guò)期,就不做任何刷新處理,使用之前的token值來(lái)請(qǐng)求
然后就是getNewToken()這個(gè)方法,這個(gè)方法返回一個(gè)新的access_token

private fun getNewToken(): String {
        var newToken: String
        var refreshToken: String
        synchronized(TokenInterceptor::class.java) {
            //刷新access_token的接口請(qǐng)求
            val refreshTokenR = retrofitApi().create(RefreshTokenR::class.java)
            val refresh = SPUtils.getString(App.getInstance().baseContext, REFRESH_TOKEN, REFRESH_TOKEN, "")
            val call = refreshTokenR.toRefreshToken(refresh)
            val execute = call.execute()
            newToken = execute.body()!!.data.access_token
            refreshToken = execute.body()!!.data.refresh_token
            SPUtils.setString(App.getInstance().baseContext, REFRESH_TOKEN, REFRESH_TOKEN, refreshToken)
        }
        return newToken
    }

主要的流程就是請(qǐng)求刷新token的接口,獲取到新的token值
其中RefreshTokenR就是Retrofit中注解參數(shù)的接口部分

interface RefreshTokenR {

    @FormUrlEncoded
    @POST(refreshToken)
    fun toRefreshToken(@Field("refresh_token") refresh_token: String): Call<NewTokenData>
}

retrofitApi()就是Retrofit的請(qǐng)求部分

private fun retrofitApi(): Retrofit {
        return Retrofit.Builder()
                .baseUrl(URL_IP)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }

因?yàn)榇a封裝的原因,無(wú)法使用封裝好的部分,只好新建一個(gè)方法來(lái)做處理了
這些就是自定義攔截類TokenInterceptor的全部?jī)?nèi)容,然后在封裝好的Retrofit類中添加攔截器

public <T> T getRetrofit(String baseUrl, Class<T> t, String token) {
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.level(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .retryOnConnectionFailure(true)
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .addInterceptor(new TokenInterceptor(token));//自定義攔截器
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(builder.build())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        return retrofit.create(t);

    }

這里有一點(diǎn)要注意
在未添加自定義攔截器的時(shí)候,是直接new的一個(gè)自帶的攔截器,當(dāng)自定義了攔截器后,需要把之前的攔截器給刪掉,不然就會(huì)像我一樣出現(xiàn)奇怪的問(wèn)題,導(dǎo)致我想了半天,找了半天原因,在出現(xiàn)原因的時(shí)候,找了很久,突發(fā)奇想屏蔽掉之前的攔截器,在運(yùn)行成功的時(shí)候,我的心態(tài)是崩潰的,事后想了下,才真正了解了攔截器的真正用意
,下面給出TokenInterceptor的完整代碼

class TokenInterceptor(private var token: String) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        //當(dāng)前時(shí)間
        val nowTime = System.currentTimeMillis()
        //登錄成功時(shí)的時(shí)間-當(dāng)前時(shí)間得到時(shí)間差
        val timeDifference = SPUtils.getLong(App.getInstance().baseContext, TOKEN_TIME, TOKEN_TIME, 0) - nowTime
        if ((App.getInstance().access_token.isEmpty()) or (timeDifference > 86399)) {
            //如果access_token為空,或者 access_token有效期超過(guò)24小時(shí),需要刷新token
            //獲取新的token
            val newToken = getNewToken()
            SPUtils.setLong(App.getInstance().baseContext, TOKEN_TIME, TOKEN_TIME, System.currentTimeMillis())
            Log.e("newToken:", newToken)
            App.getInstance().access_token = newToken
            val newRequest = chain.request()
                    .newBuilder()
                    .addHeader("Authorization", "Bearer $newToken")
                    .build()
            return chain.proceed(newRequest)
        } else {
            val oldRequest = chain.request()
                    .newBuilder()
                    .addHeader("Authorization", "Bearer $token")
                    .build()
            return chain.proceed(oldRequest)
        }
    }

    private fun getNewToken(): String {
        var newToken: String
        var refreshToken: String
        synchronized(TokenInterceptor::class.java) {
            //刷新access_token的接口請(qǐng)求
            val refreshTokenR = retrofitApi().create(RefreshTokenR::class.java)
            val refresh = SPUtils.getString(App.getInstance().baseContext, REFRESH_TOKEN, REFRESH_TOKEN, "")
            val call = refreshTokenR.toRefreshToken(refresh)
            val execute = call.execute()
            newToken = execute.body()!!.data.access_token
            refreshToken = execute.body()!!.data.refresh_token
            SPUtils.setString(App.getInstance().baseContext, REFRESH_TOKEN, REFRESH_TOKEN, refreshToken)
        }
        return newToken
    }

    private fun retrofitApi(): Retrofit {
        return Retrofit.Builder()
                .baseUrl(URL_IP)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丐枉,一起剝皮案震驚了整個(gè)濱河市哆键,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瘦锹,老刑警劉巖籍嘹,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異弯院,居然都是意外死亡辱士,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門听绳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)颂碘,“玉大人,你說(shuō)我怎么就攤上這事椅挣⊥凡恚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵鼠证,是天一觀的道長(zhǎng)峡竣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)量九,這世上最難降的妖魔是什么适掰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任颂碧,我火速辦了婚禮,結(jié)果婚禮上类浪,老公的妹妹穿的比我還像新娘载城。我一直安慰自己,他們只是感情好戚宦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布个曙。 她就那樣靜靜地躺著,像睡著了一般受楼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呼寸,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天艳汽,我揣著相機(jī)與錄音,去河邊找鬼对雪。 笑死河狐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瑟捣。 我是一名探鬼主播馋艺,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼迈套!你這毒婦竟也來(lái)了捐祠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桑李,失蹤者是張志新(化名)和其女友劉穎踱蛀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贵白,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡率拒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了禁荒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猬膨。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呛伴,靈堂內(nèi)的尸體忽然破棺而出勃痴,到底是詐尸還是另有隱情,我是刑警寧澤磷蜀,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布召耘,位于F島的核電站,受9級(jí)特大地震影響褐隆,放射性物質(zhì)發(fā)生泄漏污它。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衫贬。 院中可真熱鬧德澈,春花似錦、人聲如沸固惯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)葬毫。三九已至镇辉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贴捡,已是汗流浹背忽肛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烂斋,地道東北人屹逛。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像汛骂,于是被迫代替她去往敵國(guó)和親罕模。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,101評(píng)論 1 32
  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中帘瞭。淑掌。。 其實(shí)如果沒(méi)有APIManager這一層是沒(méi)法使用delegate的图张,畢竟多個(gè)單...
    yhtang閱讀 5,193評(píng)論 1 23
  • 博文出處:一起來(lái)寫(xiě)OKHttp的攔截器锋拖,歡迎大家關(guān)注我的博客,謝謝祸轮!00:00一開(kāi)始就不多說(shuō)廢話了兽埃,主要因?yàn)楣ぷ鲿r(shí)...
    加油碼農(nóng)閱讀 3,996評(píng)論 1 7
  • 天氣,越來(lái)越熱了适袜。而我恨不得每天晚上都沖一個(gè)涼水澡柄错。人都感覺(jué)那么熱,更別說(shuō)動(dòng)物了苦酱。最近不知道我家的小貓?jiān)趺戳耍?..
    余生只陪你閱讀 98評(píng)論 0 0
  • 有很多朋友或許都有一個(gè)疑問(wèn) 如果空間比較小該如何合理的偷空間呢? 承重墻到底能不能動(dòng)呢扯饶? 答案肯定是 空間改造的基...
    ZY空間改造專家閱讀 388評(píng)論 0 0