axios请求头Token管理:从安全实践到性能优化

1次阅读
没有评论

共计 2376 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景痛点分析

在现代前端应用中,JWT Token 作为身份验证的常见方式,通常需要通过请求头传递给后端。但这种做法存在两个主要问题:

axios 请求头 Token 管理:从安全实践到性能优化

  1. 安全风险 :Token 直接暴露在请求头中,容易被中间人攻击或通过 XSS 漏洞窃取
  2. 并发问题 :当 Token 过期需要刷新时,多个并发请求可能同时触发刷新逻辑,导致 Token 竞争

技术方案对比

1. 手动添加 headers

// 每次请求都需要重复写 headers
axios.get('/api/data', {headers: { Authorization: `Bearer ${token}` }
})
  • 优点:简单直接
  • 缺点:代码重复,容易遗漏,维护困难

2. axios 拦截器统一注入

// 请求拦截器示例
axios.interceptors.request.use(config => {config.headers.Authorization = `Bearer ${getToken()}`
  return config
})
  • 优点:集中管理,避免重复代码
  • 缺点:需要处理 Token 刷新等复杂场景

3. 结合状态管理

// 结合 Redux/Vuex 的示例
store.subscribe(() => {const token = store.getState().auth.token
  axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
})
  • 优点:与应用状态深度集成
  • 缺点:增加了架构复杂度

核心代码实现

完整的 axios 拦截器配置

import axios from 'axios'
import {refreshToken} from './auth'

let isRefreshing = false
let failedQueue: any[] = []

const processQueue = (error: any, token?: string) => {
  failedQueue.forEach(prom => {if (error) {prom.reject(error)
    } else {prom.resolve(token)
    }
  })
  failedQueue = []}

axios.interceptors.request.use(config => {const token = localStorage.getItem('token')
  if (token) {config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, error => {return Promise.reject(error)
})

axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config

    if (error.response.status === 401 && !originalRequest._retry) {if (isRefreshing) {return new Promise((resolve, reject) => {failedQueue.push({ resolve, reject})
        }).then(token => {originalRequest.headers.Authorization = `Bearer ${token}`
          return axios(originalRequest)
        }).catch(err => {return Promise.reject(err)
        })
      }

      originalRequest._retry = true
      isRefreshing = true

      try {const newToken = await refreshToken()
        localStorage.setItem('token', newToken)
        axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`
        processQueue(null, newToken)
        return axios(originalRequest)
      } catch (err) {processQueue(err)
        return Promise.reject(err)
      } finally {isRefreshing = false}
    }

    return Promise.reject(error)
  }
)

性能优化

  1. 减少 Token 验证请求
  2. 对静态资源请求禁用拦截器
  3. 实现 Token 有效期缓存检查

  4. 请求队列管理

  5. 如上述代码所示,使用队列处理并发刷新场景
  6. 设置合理的超时时间(建议 5 -10 秒)

安全考量

HttpOnly Cookie vs Header

方案 优点 缺点
Header 适合前后端分离架构 易受 XSS 攻击
HttpOnly Cookie 防止 XSS 窃取 需处理 CSRF 防护

CSRF 防护方案

// 双重提交 Cookie 模式
axios.interceptors.request.use(config => {const csrfToken = getCSRFToken() // 从 meta 标签或 Cookie 获取
  config.headers['X-CSRF-TOKEN'] = csrfToken
  return config
})

生产环境避坑指南

  1. 避免 localStorage 存储风险
  2. 考虑使用 sessionStorage
  3. 或实现内存存储 + 定时刷新

  4. SSR 场景处理

  5. 服务端请求使用单独的 axios 实例
  6. 通过 Cookie 传递 Token

总结与思考

通过 axios 拦截器管理 Token 是一个平衡安全性和开发效率的解决方案。但在实际项目中,我们还需要考虑:

  • 如何实现无感刷新 Token?
  • 在微前端架构下如何共享认证状态?
  • 是否可以考虑使用 OAuth 2.0 的静默刷新机制?

这些问题的答案可能因项目架构而异,但核心原则始终是:在保证安全的前提下,提供最佳的用户体验。

正文完
 0
评论(没有评论)