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

Nuxt.js/Vue.js中Vuex存储的Axios认证Token刷新后重置问题排查

分析与解决方案

首先,咱们来梳理下你遇到的核心问题:页面刷新后,虽然nuxtServerInit调用了fetch,但Token没有正确同步到客户端的Axios实例,同时服务端获取Cookie的方式也存在上下文不一致的风险。下面是具体的问题点和修复方案:

问题1:服务端获取Cookie的上下文错误

在nuxtServerInit中你拿到了当前请求的req,但后续的fetch和fetchToken并没有传递这个req,而是用this.app.context.req来获取Cookie。在服务端渲染场景下,每个请求的上下文是独立的,this.app.context可能指向的不是当前请求的上下文,导致无法正确获取到当前用户的Token Cookie。

修复方式:传递请求上下文

修改nuxtServerInit、fetch和fetchToken,把req参数逐层传递下去:

export const actions = {
  async nuxtServerInit ({ dispatch }, { req }) {
    // 将req传递给fetch action
    await dispatch('fetch', { req })
  },

  // Fetch Token
  async fetchToken ({ dispatch }, { req }) {
    let token
    // 服务端直接使用传入的req获取Cookie,客户端用document.cookie
    const cookieStr = process.browser ? document.cookie : req.headers.cookie
    const cookies = Cookie.parse(cookieStr || '') || {}
    token = cookies['ccmsToken']

    if (token) {
      await dispatch('updateToken', token)
    }
  },

  // Fetch
  async fetch ({ commit, dispatch }, { req, username = 'admin', endpoint = 'http://localhost:8000/api/user' } = {}) {
    // 将req传递给fetchToken
    await dispatch('fetchToken', { req })

    if (!this.state.token) {
      return
    }

    try {
      const data = await this.$axios.$get(`${endpoint}?username=${username}`)
      commit('setUser', data)
    } catch (e) {
      await dispatch('reset')
    }
  },

  // Login
  async login ({ dispatch }, { fields, endpoint = 'http://localhost:8000/api/login' } = {}) {
    try {
      const data = await this.$axios.$post(endpoint, fields)
      if (data.success) {
        await dispatch('updateToken', data.token)
        // 注意这里调用fetch要传对象格式的参数
        await dispatch('fetch', { username: data.user.username })
      } else {
        throw new Error(data.message)
      }
    } catch (error) {
      if (error.response?.status === 401) {
        throw new Error('Bad credentials')
      }
      throw error
    }
  },

  // ...其他actions保持不变
}

问题2:客户端Axios实例未同步Token

页面刷新时,服务端的nuxtServerInit会把Token存入store并设置服务端的Axios Token,但客户端的Axios实例是独立的,不会自动同步这个Token。导致客户端发起请求时没有带上认证头。

修复方式:客户端初始化时设置Axios Token

创建一个客户端专属的Nuxt插件,在客户端初始化时从store中读取Token并设置到Axios实例:

  1. 在plugins/axios-token.js中编写插件代码:
export default function ({ store, $axios }) {
  // 仅在客户端运行,从store获取Token并设置到Axios
  if (process.browser && store.state.token) {
    // 第二个参数是认证scheme,根据你的后端要求调整,比如'Bearer'
    $axios.setToken(store.state.token, '')
  }
}
  1. 在nuxt.config.js中注册这个插件:
export default {
  // ...其他配置
  plugins: [
    // 其他插件...
    { src: '~/plugins/axios-token.js', mode: 'client' } // 指定仅在客户端运行
  ]
}

问题3:登出时Cookie过期时间设置错误

在updateToken的服务端逻辑中,当Token为null时,你设置的过期时间是明天,这会导致Cookie不会被删除,应该设置为过去的时间:

// ...updateToken中的服务端逻辑
} else {
  // ...Server
  let params = { path: '/' } // 建议明确设置path,避免Cookie作用域问题
  if (!token) {
    const date = new Date()
    date.setDate(date.getDate() - 1) // 设置为昨天,让Cookie立即过期
    params.expires = date
    params.maxAge = -1 // 额外设置maxAge确保兼容性
  }
  this.app.context.res.setHeader('Set-Cookie', Cookie.serialize('ccmsToken', token || '', params))
}

额外优化点

  • this.$axios.setToken(token, '')中的第二个参数是认证scheme,如果你的后端要求使用Bearer,建议改为$axios.setToken(token, 'Bearer'),避免认证失败。
  • 在fetch中拼接URL参数时,建议使用URLSearchParams来避免编码问题:
const params = new URLSearchParams({ username })
const data = await this.$axios.$get(endpoint, { params })

完成这些修改后,刷新页面时,服务端会正确从请求Cookie中获取Token并同步到store,客户端初始化时也会把store中的Token设置到Axios实例,这样就能保证刷新后请求依然带有认证头了。

内容的提问来源于stack exchange,提问作者Karolis Stakėnas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:11:25