基于OkHttp拦截器实现Token自动刷新(kotlin实现)
OKHttp拦截器处理Token过期
通过拦截器,获取返回的数据
判断token是否过期
如果token过期则刷新token
使用最新的token,重新请求网络数据
代码实现
自定义RefreshTokenInterceptor继承Interceptor,处理以上问题
class TokenOutInterceptor : Interceptor {
private val gson: Gson by lazy { Gson() }
private val lock = Any()
@kotlin.jvm.Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
return if (response.body() != null && response.body()!!.contentType() != null) {
val mediaType = response.body()!!.contentType()
val string = response.body()!!.string()
val responseBody = ResponseBody.create(mediaType, string)
try {
val apiResponse = gson.fromJson(string, ApiResponse::class.java)
//2001 token失效
if (apiResponse.code == 2001) {
//同步防止并发 请求刷新token
synchronized(lock) {
refreshToken()
}
//使用新的Token,创建新的请求
val newRequest = chain.request()
.newBuilder()
.header("token", CacheUtil.getToken())
.build()
chain.proceed(newRequest)
} else {
response.newBuilder().body(responseBody).build()
}
} catch (ex: Exception) {
response
}
} else {
response
}
}
/**
* 同步请求刷新Token
*/
private fun refreshToken() {
val params: MutableMap<String, Any> = mutableMapOf()
params["refreshToken"] = CacheUtil.getRefreshToken()
val jsonStr = Gson().toJson(params)
val requestBody: RequestBody =
RequestBody.create(MediaType.parse(CONTENT_TYPE), jsonStr)
val request = Request.Builder()
.url(BuildConfig.BASE_URL + ActionConstant.ACTION_REFRESH_TOKEN)
.addHeader("deviceType", "android")//header添加根据自己项目需要
.addHeader("deviceId", DeviceIdUtil.getDeviceId(appContext))
.post(requestBody)
.build()
val call = OkHttpClient.Builder().build().newCall(request)
val response = call.execute()
val result = response.body()?.string()
val tokenBean: RefreshTokenDto = Gson().fromJson(result, RefreshTokenDto::class.java)
when (tokenBean.code) {
200 -> {
//保存最新token
CacheUtil.setToken(tokenBean.data.token)
}
else -> {
//刷新token失败,或者RefreshToken 以及token都失效,就重新登录app
CacheUtil.setToken(null)
CacheUtil.setRefreshToken(null)
CacheUtil.setUser(null)
CacheUtil.setIsLogin(false)
//如果是普通的activity话 可以直接跳转,如果是navigation中的fragment,可以发送通知跳转
appContext.startActivity(Intent(appContext, LoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}
}
}
}
刷新token接口返回RefreshTokenDto
data class RefreshTokenDto(
val code: Int,
val success: Boolean,
val data: RefreshTokenBeanDto,
)
data class RefreshTokenBeanDto(
var token: String = "",
)
添加拦截器