在运用端和后端交互时,有些特定的接口需求验证token才能回来正确的成果,例如修正用户信息、获取用户信息等。token通常会设定一个有效期,在有效期内运用才能正常获取成果。当token过期时,用户体会或许受到影响,能够在token过期后主动改写token优化用户体会。
本文简单介绍一下如何运用Interceptor
和协程完成主动改写token。
完成主动改写token
运用端通常会在恳求一个需求校验token的接口失利后才感知到token过期。为了防止用户体会受到影响,能够运用OkHttp
的Interceptor
来阻拦并修正发送恳求和接收呼应的进程。
完成方案如下:
- 自定义
Interceptor
,为需求验证token的接口增加header。 - 获取呼应,判别token是否过期。
- 假如token过期,发起恳求改写token。假如多个需求验证token的接口并发履行,应该仅履行一次改写token的操作。
- 改写token后,带着新token从头发送原恳求获取呼应。
Interceptor
示例代码如下:
class ApiInterceptor : Interceptor {
// 需求验证token的接口的路径调集
private val checkTokenInterface = arrayListOf(
"/example/user/info,
"/example/user/info/edit"
)
// 是否正在改写token
// 运用Volatile,确保多线程获取的值共同
@Volatile
private var refreshingToken = false
private val authHeaderKey = "token-key"
override fun intercept(chain: Interceptor.Chain): Response {
// 依据接口路径判别是否需求验证token
val checkToken = checkTokenInterface.contains(chain.request().url.encodedPath)
// 需求验证token则增加恳求头,不需求则保持原样
var request = if (checkToken) {
chain.request().newBuilder()
.addHeader(authHeaderKey, "tokenValue")
.build()
} else {
chain.request()
}
// 获取呼应
var response = chain.proceed(request)
if (checkToken) {
// 需求验证token,处理呼应,判别token是否过期
response.body?.let { responseBody ->
try {
if (判别token过期的条件) {
runBlocking {
if (!refreshingToken) {
// 标记为正在改写token
refreshingToken = true
async {
refreshToken(chain).takeIf { it.isNotEmpty() && it.isNotBlank() }?.let { newToken ->
// 保存newToken
}
// 标记为改写token已结束
refreshingToken = false
}.start()
}
// async{}.await()会等候办法块内的代码履行完毕。
// 等到refreshingToken为false时才会履行后续代码。
if (async { stopRefreshingToken() }.await()) {
// 带着newToken履行原恳求获取呼应
response = chain.proceed(chain.request().newBuilder()
.addHeader(authHeaderKey, "newToken")
.build())
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// 回来终究的呼应成果。
// 改写token也或许失利,此刻回来原呼应,能够让用户从头登录。
return response
}
private fun refreshToken(chain: Interceptor.Chain): String {
// 创立改写token的恳求
val refreshTokenRequest = Request.Builder()
.url("refresh token url")
.addHeader(authHeaderKey, "old token")
.get()
.build()
return try {
// 获取呼应并解析取得新token
val refreshTokenResponse = chain.proceed(refreshTokenRequest)
refreshTokenResponse.token
} catch (e: IOException) {
// 失利时回来空字符串
""
}
}
private suspend fun stopRefreshingToken(): Boolean {
return if (refreshingToken) {
// 仍在改写token中,推迟1秒后再次履行此办法
delay(1000)
stopRefreshingToken()
} else {
true
}
}
}
PS: 部分代码涉及到公司的接口所以省略了,实际运用时需求针对需求进行适当的调整。