Android — 使用Interceptor和协程实现自动刷新token

在应用端和后端交互时,有些特定的接口需要验证token才能返回正确的结果,例如修改用户信息、获取用户信息等。token通常会设定一个有效期,在有效期内使用才能正常获取结果。当token过期时,用户体验可能受到影响,可以在token过期后自动刷新token优化用户体验。

本文简单介绍一下如何使用Interceptor和协程实现自动刷新token。

实现自动刷新token

应用端通常会在请求一个需要校验token的接口失败后才感知到token过期。为了避免用户体验受到影响,可以使用OkHttpInterceptor 来拦截并修改发送请求和接收响应的过程。

实现方案如下:

  1. 自定义Interceptor,为需要验证token的接口添加header。
  2. 获取响应,判断token是否过期。
  3. 如果token过期,发起请求刷新token。如果多个需要验证token的接口并发执行,应该仅执行一次刷新token的操作。
  4. 刷新token后,携带新token重新发送原请求获取响应。

Interceptor示例代码如下:

scss 复制代码
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: 部分代码涉及到公司的接口所以省略了,实际使用时需要针对需求进行适当的调整。

相关推荐
2501_9371454137 分钟前
IPTV电视源码系统2026优化版:技术升级,全场景流畅适配
android·电视盒子·源代码管理
Ehtan_Zheng2 小时前
让你的代码更整洁:10 个必知的 Kotlin 扩展函数
android
城东米粉儿2 小时前
Android VSync 笔记
android
城东米粉儿2 小时前
Android SurfaceFlinger 笔记
android
似霰2 小时前
Android 日志系统5——logd 写日志过程分析二
android·log
hewence12 小时前
Kotlin CoroutineContext 详解
android·开发语言·kotlin
Albert Edison2 小时前
【Python】文件
android·服务器·python
大模型玩家七七3 小时前
效果评估:如何判断一个祝福 AI 是否“走心”
android·java·开发语言·网络·人工智能·batch
Aurora4193 小时前
Android事件分发逻辑--针对事件分发相关函数的讲解
android