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

Flutter开发中遇到401错误时如何刷新token并重试请求

401状态下自动刷新Token并重试请求的规范实现方案

核心设计前提

这套逻辑的基础是你的身份鉴权体系采用双Token机制:

  • 短有效期的access_token:用于普通接口请求的身份校验,过期时间通常设置为1-2小时
  • 长有效期的refresh_token:仅用于刷新access_token,过期时间通常设置为7-30天

标准实现步骤

  • 请求拦截层统一挂载身份标识
    所有业务请求发出前,统一在请求头添加Authorization: Bearer <access_token>,refresh token禁止在普通业务请求中携带,仅可在刷新Token的专用接口中使用。
  • 响应拦截层捕获401状态做分层处理
    收到响应后优先判断状态码:
    • 非401状态直接走正常业务响应逻辑
    • 触发401时,先校验当前是否存在正在进行的刷新Token请求:
      1. 无正在刷新的请求:立即标记「刷新中」状态,调用刷新Token接口,拿到新的双Token后更新本地存储,清空「刷新中」标记,依次重新发起所有暂存的待重试请求
      2. 有正在刷新的请求:将当前请求的所有配置存入待重试队列,返回Pending状态的Promise,等待刷新完成后自动触发重试
  • 异常兜底逻辑
    若刷新Token接口本身返回401/403,说明refresh_token也已过期,直接清空本地所有身份凭证,跳转至登录页,同时清空待重试队列终止所有重试逻辑。
  • 重试次数限制
    给每个请求添加最多1次的重试上限,避免逻辑异常导致无限循环请求。

基于Axios的示例代码

import axios from 'axios'

const instance = axios.create({
  baseURL: '/api',
  timeout: 10000
})

// 刷新状态标记
let isRefreshing = false
// 待重试请求队列
let retryRequests = []

// 请求拦截器
instance.interceptors.request.use(
  config => {
    const token = localStorage.getItem('access_token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  error => Promise.reject(error)
)

// 响应拦截器
instance.interceptors.response.use(
  response => response.data,
  async error => {
    const { config, response } = error
    // 非401错误直接抛出
    if (!response || response.status !== 401) {
      return Promise.reject(error)
    }
    // 刷新接口本身返回401直接跳登录,避免死循环
    if (config.url === '/auth/refresh') {
      localStorage.clear()
      window.location.href = '/login'
      return Promise.reject(error)
    }
    // 已有重试标记的请求直接抛出,避免无限重试
    if (config.__retry) {
      return Promise.reject(error)
    }

    if (!isRefreshing) {
      isRefreshing = true
      try {
        const refreshToken = localStorage.getItem('refresh_token')
        const res = await axios.post('/api/auth/refresh', { refresh_token: refreshToken })
        const { access_token, refresh_token } = res.data
        // 更新本地存储的Token
        localStorage.setItem('access_token', access_token)
        localStorage.setItem('refresh_token', refresh_token)
        // 执行所有待重试请求
        retryRequests.forEach(cb => cb(access_token))
        retryRequests = []
        // 标记当前请求已重试
        config.__retry = true
        // 重新发起当前请求
        return instance(config)
      } catch (e) {
        // 刷新失败跳登录
        localStorage.clear()
        window.location.href = '/login'
        return Promise.reject(e)
      } finally {
        isRefreshing = false
      }
    } else {
      // 正在刷新中,将请求加入队列
      return new Promise(resolve => {
        retryRequests.push(token => {
          config.headers.Authorization = `Bearer ${token}`
          config.__retry = true
          resolve(instance(config))
        })
      })
    }
  }
)

export default instance

注意事项

  • refresh token安全处理:Web端建议将refresh token存储在HttpOnly、Secure属性的Cookie中,避免XSS攻击窃取,不要存在localStorage/sessionStorage中。
  • 幂等性校验:非幂等请求(比如POST提交订单)重试前建议做业务层面的校验,避免重复提交导致业务异常。
  • 超时处理:刷新Token接口建议单独设置较短的超时时间,避免长时间无响应用户侧卡死。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 06:09:01