axios实战:如何优雅地为所有请求自动添加Token认证头

1次阅读
没有评论

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

image.webp

背景痛点

在前后端分离项目中,每次请求都需要携带 Token 进行身份认证。手动处理方式存在三大问题:

axios 实战:如何优雅地为所有请求自动添加 Token 认证头

  • 重复代码 :每个请求都需要显式添加 headers: {Authorization: 'Bearer xxx'}
  • 维护困难 :Token 变更时需要修改所有请求调用处
  • 安全风险 :容易遗漏添加导致接口鉴权失败

技术方案对比

方案 1:手动添加(不推荐)

// 每个请求都需要重复写 header
axios.get('/api/data', {headers: { Authorization: 'Bearer' + store.state.token}
})

方案 2:封装 request 函数

function request(config) {
  return axios({
    ...config,
    headers: {Authorization: 'Bearer' + getToken(),
      ...config.headers
    }
  })
}

方案 3:拦截器方案(推荐)

// 一次配置,全局生效
axios.interceptors.request.use(config => {config.headers.Authorization = `Bearer ${token}`
  return config
})

优势对比
– 手动添加:维护成本最高
– 函数封装:需要改造所有调用方式
– 拦截器:无侵入式改造,集中管理

核心实现

1. 基础拦截器配置

/**
 * 请求拦截器配置
 * @param {import('axios').AxiosRequestConfig} config
 */
const requestInterceptor = (config) => {
  const token = store.state.user.token
  if (token && !config.headers.Authorization) {config.headers.Authorization = `Bearer ${token}`
    config.headers['Content-Type'] = 'application/json'
  }
  return config
}

// 注册拦截器
axios.interceptors.request.use(requestInterceptor)

2. 响应拦截器处理 401

axios.interceptors.response.use(
  response => response,
  error => {if (error.response?.status === 401) {router.push('/login')
    }
    return Promise.reject(error)
  }
)

安全实践

1. Token 刷新机制

let isRefreshing = false

axios.interceptors.response.use(null, async error => {if (error.response?.status === 401 && !isRefreshing) {
    isRefreshing = true
    try {const { token} = await refreshToken()
      store.commit('UPDATE_TOKEN', token)
      error.config.headers.Authorization = `Bearer ${token}`
      return axios(error.config)
    } finally {isRefreshing = false}
  }
  return Promise.reject(error)
})

2. 防止重复注册

// 单例模式管理拦截器
let requestInterceptorId

function setupInterceptors() {if (requestInterceptorId !== undefined) {axios.interceptors.request.eject(requestInterceptorId)
  }
  requestInterceptorId = axios.interceptors.request.use(requestInterceptor)
}

避坑指南

1. 文件上传处理

// 遇到 FormData 时移除 Content-Type
axios.interceptors.request.use(config => {if (config.data instanceof FormData) {delete config.headers['Content-Type'] // 浏览器会自动生成 multipart/form-data
  }
  return config
})

2. 内存泄漏防范

// Vue 组件卸载时
onUnmounted(() => {axios.interceptors.request.eject(requestInterceptorId)
})

完整示例

import axios from 'axios'
import store from '@/store'
import router from '@/router'

// 请求拦截器
const requestInterceptor = config => {
  const token = store.state.user.token
  if (token) {
    config.headers = {
      ...config.headers,
      Authorization: `Bearer ${token}`,
      'Content-Type': config.data instanceof FormData 
        ? undefined 
        : 'application/json'
    }
  }
  return config
}

// 响应拦截器
const errorInterceptor = async error => {
  const originalRequest = error.config

  if (error.response?.status === 401 && !originalRequest._retry) {
    originalRequest._retry = true
    try {const { token} = await refreshToken()
      store.commit('UPDATE_TOKEN', token)
      originalRequest.headers.Authorization = `Bearer ${token}`
      return axios(originalRequest)
    } catch (e) {router.push('/login?expired=1')
      return Promise.reject(e)
    }
  }

  return Promise.reject(error)
}

// 注册拦截器
const requestInterceptorId = axios.interceptors.request.use(requestInterceptor)
axios.interceptors.response.use(response => response, errorInterceptor)

// 导出清理方法
export function clearInterceptors() {axios.interceptors.request.eject(requestInterceptorId)
}

总结

通过 axios 拦截器实现 Token 自动注入,我们获得了:

  1. 开发效率提升 :无需关心每个请求的鉴权头设置
  2. 统一错误处理 :401 状态码自动跳转登录页
  3. 安全增强 :Token 刷新机制避免频繁重新登录
  4. 可维护性 :所有鉴权逻辑集中在拦截器模块

实际项目中建议结合路由守卫实现完整的鉴权流程,并注意在 SPA 应用切换用户时及时清理旧拦截器。这种模式也适用于其他需要统一处理的请求逻辑,如埋点上报、性能监控等场景。

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