You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java中使用Retrofit处理JWT刷新令牌问题咨询

Android JWT Token过期处理方案

针对你遇到的JWT Token过期问题,结合你提到的两种思路,下面给出具体实现方案,适配你的Retrofit单例和拦截器场景:

方案一:提前刷新Token

在每次请求发起前,检查access token的剩余有效期,当距离过期还有设定阈值(比如30秒)时,自动调用刷新接口获取新token,再执行原请求。

实现步骤:

  1. 确保你已将access token的过期时间存储在本地(比如SharedPreferences)
  2. 在OkHttp拦截器中添加过期检查逻辑:
    class TokenInterceptor : Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            val authPref = AppContext.getSharedPreferences("auth", Context.MODE_PRIVATE)
            val currentAccessToken = authPref.getString("access_token", "")
            val expireTimestamp = authPref.getLong("expire_time", 0)
            
            val currentTime = System.currentTimeMillis()
            // 距离过期不足30秒时触发刷新
            if (expireTimestamp - currentTime < 30 * 1000 && expireTimestamp > currentTime) {
                // 同步调用刷新接口,避免并发请求重复触发刷新
                val newTokenInfo = refreshTokenSync()
                newTokenInfo?.let {
                    authPref.edit()
                        .putString("access_token", it.accessToken)
                        .putLong("expire_time", currentTime + it.expiresIn * 1000)
                        .apply()
                }
            }
            
            // 构建带新token的请求
            val finalRequest = chain.request().newBuilder()
                .addHeader("Authorization", "Bearer ${authPref.getString("access_token", "")}")
                .build()
            return chain.proceed(finalRequest)
        }
        
        // 同步调用刷新接口(复用你的Retrofit单例)
        private fun refreshTokenSync(): TokenResponse? {
            return try {
                val refreshToken = AppContext.getSharedPreferences("auth", Context.MODE_PRIVATE)
                    .getString("refresh_token", "")
                RetrofitSingleton.apiService.refreshToken(refreshToken).execute().body()
            } catch (e: Exception) {
                e.printStackTrace()
                null
            }
        }
    }
    
  3. 额外优化:可以加一个全局锁或者标记位,防止多个并发请求同时触发刷新操作。

方案二:401错误触发Token刷新

当接口返回401 Unauthorized时,拦截该响应,调用刷新接口获取新token,然后自动重试原请求。

实现步骤:

  1. 在OkHttp拦截器中处理401响应:
    class TokenInterceptor : Interceptor {
        private val refreshLock = Any()
        private var isRefreshing = false
        
        override fun intercept(chain: Interceptor.Chain): Response {
            val originalRequest = chain.request()
            val response = chain.proceed(addTokenToRequest(originalRequest))
            
            // 处理401错误
            if (response.code == 401) {
                synchronized(refreshLock) {
                    if (!isRefreshing) {
                        isRefreshing = true
                        val authPref = AppContext.getSharedPreferences("auth", Context.MODE_PRIVATE)
                        val refreshToken = authPref.getString("refresh_token", "")
                        val tokenResponse = RetrofitSingleton.apiService.refreshToken(refreshToken).execute().body()
                        
                        tokenResponse?.let {
                            // 更新本地存储的token信息
                            authPref.edit()
                                .putString("access_token", it.accessToken)
                                .putLong("expire_time", System.currentTimeMillis() + it.expiresIn * 1000)
                                .putString("refresh_token", it.refreshToken) // 如果接口返回新的refresh token
                                .apply()
                            // 关闭原响应,用新token重试请求
                            response.close()
                            return chain.proceed(addTokenToRequest(originalRequest))
                        } ?: run {
                            // 刷新失败,清空本地token并跳转登录页
                            authPref.edit().clear().apply()
                            AppContext.startActivity(Intent(AppContext, LoginActivity::class.java).apply {
                                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                            })
                        }
                        isRefreshing = false
                    } else {
                        // 等待刷新完成后,用新token重试原请求
                        return chain.proceed(addTokenToRequest(originalRequest))
                    }
                }
            }
            return response
        }
        
        private fun addTokenToRequest(request: Request): Request {
            val accessToken = AppContext.getSharedPreferences("auth", Context.MODE_PRIVATE)
                .getString("access_token", "")
            return request.newBuilder()
                .addHeader("Authorization", "Bearer $accessToken")
                .build()
        }
    }
    
  2. 注意事项:
    • 加锁避免多个401请求同时触发刷新
    • 若refresh token也过期,直接清空本地数据并跳转登录
    • 确保刷新接口本身不会返回401,否则会陷入死循环

两种方案对比

  • 提前刷新:用户体验更流畅,无请求重试延迟,但依赖本地时间准确性,需处理设备时间偏差问题
  • 401重试:实现更简单,无需维护时间校验逻辑,但会有一次请求失败后重试的过程,可能影响瞬时体验

内容的提问来源于stack exchange,提问作者Nagendrakumar Boorapu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 10:40:36