前面介绍了kotlin之RxJava2+Retrofit2,现在就部分项目中遇到的Token刷新问题做个思路。
Token是指个别项目中引入的请求令牌,在首次登陆后会得到一个随机字符串,这个随机字符串有一定的时间限制,过期后需要使用特定的字符串去刷新这个token以保证后续的接口能够正常访问。
这里有两种解决方案,一种是使用okhttp提供Authenticator类,这种方式只适用于服务器将鉴权失败以状态码的方式返回。另一种就是添加拦截器,适用于认定鉴权失败时正常的业务请求,返回状态码依然是200。
方案一
先上干货
class TokenAuthenticator : Authenticator {
override fun authenticate(route: Route?, response: Response?): Request? {
val refreshToken = Utils.defaultSP.getString(Constants.KEY_REFRESH_TOKEN, null)
var newToken: String? = null
//这里使用同步的方式去获取新的token
if (refreshToken != null) newToken = ApiService.default().refreshToken(refreshToken).execute().body()
return if (newToken != null) {
Utils.defaultSP.edit().putString(Constants.KEY_ACCESS_TOKEN, newToken).apply()
response?.request()?.newBuilder()?.header("Authentication", newToken)?.build()
} else {
response?.request()
}
}
}
okHttpBuilder.authenticator(authenticator)
覆写Authenticator 类后,并在构建okhttpclient的时候可以添加进去。在网络请求返回后如果状态码是401则会回调authenticate方法,在这个方法中我们需要做的就是利用refresh_token字符串去获取新的Token。如果拿到了新的token则将新的token替换到原本请求头中的字段再发起一次请求,如果没有拿到新的token则返回原来的请求。
方案二
class TokenInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain?): Response {
val originalRequest = chain?.request()
val response = chain?.proceed(originalRequest)
val json = response?.body().toString()
if (!TextUtils.isEmpty(json)) {
val obj = JSONObject(json)
if (obj.has("code") && obj.getInt("code") == Constants.NET_CODE_TOKEN_EXPIRE) {
val refreshToken = Utils.defaultSP.getString(Constants.KEY_REFRESH_TOKEN, null)
var newToken: String? = null
if (refreshToken != null) newToken = ApiService.default().refreshToken(refreshToken).execute().body()
if (newToken != null) {
Utils.defaultSP.edit().putString(Constants.KEY_ACCESS_TOKEN, newToken).apply()
val requestBuilder = originalRequest?.newBuilder()?.header("Authentication", newToken)
return chain!!.proceed(requestBuilder?.build())
}
}
}
return response!!
}
}
okHttpBuilder.addInterceptor(interceptor)
以上是使用拦截器的方式来监听网络请求的结果,判断业务码是某个值时发起刷新Token的网络请求,顺利拿到新的token后替换掉原来请求头的字段,并再次发起请求。
总结
以上是目前认为比较简单的处理刷新token的方式,个人更推荐第二种方式一点,但也要结合实际的需求,如果有更好的方式,可以和我探讨我在补充出来分享给大家。