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