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

Vue+Laravel登录后路由守卫校验token返回401问题

Vue Router 全局守卫首次登录跳转401问题排查

问题背景

使用VueJS、Vue Router搭配后端Laravel开发小型项目时,多次配置导航守卫未达预期:

  • 登录组件本身运行正常:调用http://proudata.test/api/login接口,返回isLogged为true时,将响应数据存入localStorage的token字段,再通过router.push跳转至dashboard路由
  • 异常表现:登录成功后首次跳转触发token校验接口时直接返回401未授权,请求未携带存入localStorage的Bearer Token;浏览器开发者工具确认localStorage已正确写入token,手动刷新页面后所有请求可正常携带token,功能恢复正常
  • 预期逻辑:用户每次跳转页面前,调用validate-token接口校验token有效性、是否过期,未通过校验则跳转回登录页

现有实现代码

登录组件方法(Login.vue)

login() {
  axios.post('http://proudata.test/api/login', this.user).then(response => {
    if (response.data.data.isLogged == true) {
      localStorage.setItem('token', JSON.stringify(response.data));
      router.push({ name: 'dashboard' });
    }
  }).catch(error => {
    this.error = true;
    this.message = error.response.data.message
  });
}

全局前置路由守卫(routes.js)

router.beforeEach(async (routeTo, routeFrom, next) => {
  const authRequired = routeTo.matched.some((route) => route.meta.authRequired)
  if (!authRequired) return next()

  // 首次登录跳转时此处请求返回401
  await client.get('validate-token').then(() => {
    next();
  }).catch(() => {
    next({ name: 'login' })
  });
});

封装的Axios公共请求客户端(Client.js)

import axios from 'axios';

let userData = localStorage.getItem('token');

let authenticated = {data:{}};

if(userData !== null){
    authenticated = JSON.parse(userData);
}

const client = axios.create({
    baseURL: ((authenticated.data.domain !== null) ? authenticated.data.domain : 'http://domain.test/api')
});

client.interceptors.request.use(config => {
    if(userData !== null){
        config.headers.Authorization = authenticated.data.token ? `Bearer ${authenticated.data.token}` : null; 
    }
    return config;
});

export default client;

登录接口成功响应结构

{
   "data":{
     "isLogged":true,
     "token": "gkljdfslkgjdfklgjfdlkgj",
     "domain": "http://user.multitenancy.test"
   }
}

根本原因

问题出在Axios客户端的初始化逻辑:

  • userData、authenticated两个变量是在模块首次被import加载时执行赋值的,仅会读取一次当时localStorage中的值。页面首次加载时用户未登录,localStorage中无token,这两个变量会被初始化为null和空对象
  • 登录成功后虽然新的token被写入localStorage,但模块顶层的变量不会自动更新,请求拦截器中读取的始终是模块初始化时的旧空值,因此不会给请求添加Authorization头
  • 手动刷新页面后,整个JS资源重新加载,模块初始化时localStorage已经存在有效token,因此变量能拿到正确值,请求可以正常携带头信息
  • 额外问题:baseURL同样是模块初始化时固定的,首次登录返回的多租户域名不会被应用到后续请求上,刷新后才会生效

将token校验逻辑放在beforeEach全局守卫中是合理的全局鉴权实现方式,本身没有问题。

修复方案

将localStorage读取、token和baseURL的赋值逻辑全部移到请求拦截器内部,保证每次发起请求时都读取最新的本地存储数据,不要在模块顶层做一次性初始化:

import axios from 'axios';

const client = axios.create({
    // 配置默认baseURL兜底
    baseURL: 'http://domain.test/api'
});

client.interceptors.request.use(config => {
    // 每次发请求前实时读取最新的本地存储数据
    const userData = localStorage.getItem('token');
    if (userData !== null) {
        const authenticated = JSON.parse(userData);
        // 动态适配多租户域名
        if (authenticated.data?.domain) {
            config.baseURL = authenticated.data.domain;
        }
        // 动态添加鉴权头
        if (authenticated.data?.token) {
            config.headers.Authorization = `Bearer ${authenticated.data.token}`;
        }
    }
    return config;
});

export default client;

可选优化:路由守卫中混用async/await和then/catch的写法可以简化,逻辑保持一致即可:

router.beforeEach(async (routeTo, routeFrom, next) => {
  const authRequired = routeTo.matched.some((route) => route.meta.authRequired)
  if (!authRequired) return next()
  try {
    await client.get('validate-token')
    next()
  } catch {
    next({ name: 'login' })
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:03:18