共计 2338 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
在 Android 开发中,Token 的动态更新是一个常见但容易被忽视的问题。Token 过期、并发请求导致的竞争条件、网络延迟等问题,都可能引发用户体验下降甚至安全隐患。具体来说,我们经常会遇到以下几种情况:

- Token 过期导致用户突然被登出,需要重新登录
- 多个并发请求同时检测到 Token 过期,触发多次 Token 刷新
- 网络延迟导致 Token 刷新失败,后续请求无法正常执行
这些问题不仅影响用户体验,还可能带来安全风险,比如 Token 泄露或被恶意利用。
技术选型
针对 Token 动态更新,常见的实现方式有以下几种:
- Interceptor 方案 :利用 OkHttp 的 Interceptor 机制,在请求发出前检查 Token,过期时自动刷新
- Repository 层封装 :在数据层统一处理 Token 相关逻辑
- Jetpack WorkManager:使用 WorkManager 调度 Token 刷新任务
每种方案都有其优缺点:
- Interceptor 方案实现简单,但对并发控制的处理需要额外注意
- Repository 层封装逻辑清晰,但需要所有网络请求都经过 Repository
- WorkManager 适合定期刷新 Token,但不适合请求时的即时刷新
综合考虑实现复杂度和实际效果,我们推荐使用 Interceptor 方案,它既能满足大多数场景需求,又能保持代码的简洁性。
核心实现
1. 使用 OkHttp Interceptor 实现 Token 自动刷新
Interceptor 是 OkHttp 提供的一个强大功能,允许我们在请求发出前和响应返回后插入自定义逻辑。我们可以利用这个特性来实现 Token 的自动刷新。
基本思路是:
- 在请求发出前检查 Token 是否过期
- 如果过期,先刷新 Token,然后再用新 Token 重试原始请求
- 如果 Token 有效,直接放行请求
2. 处理并发请求时的 Token 竞争问题
当多个请求同时检测到 Token 过期时,可能会触发多次 Token 刷新,这不仅浪费资源,还可能导致问题。我们需要实现一个机制,确保同一时间只有一个 Token 刷新请求在执行,其他请求等待刷新完成。
可以通过以下几种方式实现:
- 使用同步锁(synchronized)保护 Token 刷新代码块
- 使用协程的 Mutex 实现更轻量级的同步
- 使用单例模式管理 Token 刷新状态
3. 错误重试机制与指数退避策略
Token 刷新可能会因为网络问题失败,我们需要实现合理的重试机制。指数退避是一种常用的策略,即每次重试的间隔时间按指数增长,避免短时间内大量重试请求压垮服务器。
代码示例
以下是一个使用 Kotlin 实现的 Token 刷新 Interceptor 示例:
class TokenRefreshInterceptor(private val tokenManager: TokenManager) : Interceptor {private val refreshLock = Any()
override fun intercept(chain: Interceptor.Chain): Response {val originalRequest = chain.request()
val initialToken = tokenManager.getToken()
// 如果 Token 不存在或已过期,先刷新 Token
if (initialToken == null || tokenManager.isTokenExpired(initialToken)) {synchronized(refreshLock) {val currentToken = tokenManager.getToken()
// 再次检查,防止其他线程已经刷新了 Token
if (currentToken == null || tokenManager.isTokenExpired(currentToken)) {
try {val newToken = tokenManager.refreshToken()
tokenManager.saveToken(newToken)
} catch (e: Exception) {
// 处理刷新失败的情况
throw TokenRefreshException("Failed to refresh token", e)
}
}
}
}
// 使用最新的 Token 构建请求
val latestToken = tokenManager.getToken()
val authenticatedRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer $latestToken")
.build()
return chain.proceed(authenticatedRequest)
}
}
性能与安全
在实现 Token 动态更新时,我们需要特别注意性能和安全性:
- 加密存储 :Token 应该加密存储在 SharedPreferences 或更安全的存储方案中
- 防重放攻击 :可以考虑为每个请求添加 nonce 或时间戳
- 频率限制 :防止短时间内频繁刷新 Token,可以设置最小刷新间隔
避坑指南
在实际开发中,我们可能会遇到以下问题:
- Token 泄露 :确保 Token 只在必要的时候存储在内存中,使用后及时清理
- 重复刷新 :如前面所述,需要妥善处理并发场景
- 刷新失败处理 :应该有合理的降级方案,比如跳转到登录界面
总结与思考
Token 动态更新是 Android 应用安全架构中的重要一环。通过 Interceptor 实现自动刷新,结合并发控制和错误处理,我们可以构建一个健壮的 Token 管理系统。但技术方案总是有改进空间的:
- 如何进一步减少 Token 刷新对用户体验的影响?
- 是否有更安全的 Token 存储和传输方案?
- 如何平衡安全性和性能需求?
这些问题值得我们在实际开发中不断探索和优化。
