You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Vue3 路由守卫中Pinia无法正确读取已认证状态的问题(Laravel Sanctum Cookie认证场景)

Vue3 路由守卫中Pinia无法正确读取已认证状态的问题(Laravel Sanctum Cookie认证场景)

我之前也遇到过几乎一模一样的问题!核心原因就是你猜的那样——路由守卫的执行时机早于用户异步数据的加载完成时机:当beforeEnter触发时,setUser里的getCurrentUser请求还没返回,Pinia里的user还是null,所以isAuthorizedfalse;等请求完成后Pinia数据更新了,但守卫已经执行过了,自然不会触发重定向。

结合Laravel Sanctum的Cookie认证场景,我给你两个可行的解决方案,你可以根据自己的需求选:

方案一:等待用户数据加载完成再挂载应用(简单直接)

这个方案会让应用在挂载前先完成用户身份的初始化,好处是路由守卫触发时数据肯定已经就绪,缺点是如果getCurrentUser请求慢,应用会短暂白屏。

步骤1:修改main.js,等待用户数据加载

setUser的调用改成异步等待,需要用async函数包裹初始化逻辑:

// main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './style.css';
import setUser from '@/plugins/set-user.js'

// 用async函数包裹初始化流程
async function initApp() {
  const app = createApp(App)
  const pinia = createPinia()

  app.use(pinia)
  app.use(router)

  // 等待用户数据加载完成再挂载应用
  await setUser(pinia)

  app.mount('#app')
}

// 执行初始化
initApp()

步骤2:修正路由守卫里的小错误

你之前的Register路由守卫里多写了.value,Pinia的store实例会自动解包computed属性,直接访问store.isAuthorized即可:

// router/index.js 里的Register路由beforeEnter
beforeEnter: (to, from, next) => {
  const userStore = useUserStore()
  // 去掉多余的.value
  if (userStore.isAuthorized) {
    next('/')
  } else {
    next()
  }
},

这样修改后,应用会等用户数据加载完再启动,路由守卫就能拿到正确的isAuthorized状态了。


方案二:全局路由守卫+异步数据预加载(用户体验更好)

如果不想让应用因为请求慢而白屏,可以用全局前置守卫来处理:先判断用户数据是否加载,没加载就先等请求完成,再处理权限逻辑。这个方案不会阻塞应用挂载,用户能看到页面骨架或者加载动画。

步骤1:给Pinia Store添加数据加载状态和异步获取方法

先在user.js里新增标记用户数据是否加载完成的状态,以及直接封装fetchUser的方法:

// store/user.js
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
import { getCurrentUser } from '@/api/user.js'

export const useUserStore = defineStore('user', () => {
  const user = ref(null)
  const isUserLoaded = ref(false) // 标记用户数据是否加载完成

  const isAuthorized = computed(() => !!user.value)

  // 新增:异步获取用户数据的方法
  async function fetchUser() {
    try {
      const userData = await getCurrentUser()
      updateUser(userData)
    } catch (err) {
      // 请求失败(比如未认证返回401),清空用户数据
      updateUser(null)
    } finally {
      // 不管成功失败,都标记为已加载
      isUserLoaded.value = true
    }
  }

  function updateUser(userData) {
    user.value = userData ?? null
  }

  return {
    user,
    isAuthorized,
    updateUser,
    fetchUser,
    isUserLoaded
  }
})

步骤2:修改路由,用全局守卫统一处理权限

把原来的beforeEnter逻辑移到全局前置守卫里,确保每次路由跳转都先等用户数据加载完成:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import LoginView from '@/views/auth/LoginView.vue'
import RegisterView from '@/views/auth/RegisterView.vue'
import { useUserStore } from '@/store/user.js'

const routes = [
  {
    name: 'Login',
    path: '/login',
    component: LoginView,
  },
  {
    name: 'Register',
    path: '/register',
    component: RegisterView,
  },
  // ... 你的其他路由
];

const router = createRouter({
  history: createWebHistory(),
  routes
})

// 全局前置守卫:统一处理用户数据加载和路由权限
router.beforeEach(async (to, from, next) => {
  const store = useUserStore()

  // 如果用户数据还没加载,先等待加载完成
  if (!store.isUserLoaded) {
    await store.fetchUser()
  }

  // 处理登录/注册页面的权限:已认证用户自动跳转到首页
  if (['Login', 'Register'].includes(to.name) && store.isAuthorized) {
    next('/')
  } else {
    // 其他正常路由直接放行
    next()
  }
})

// 保留你的routerPush方法
export async function routerPush(name, params) {
  return await router.push(params ? { name, params } : { name })
}

export default router

步骤3:简化main.js,移除原来的setUser调用

现在用户数据的加载由路由守卫处理,main.js里不需要再调用setUser了:

// main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './style.css';

const app = createApp(App)
const pinia = createPinia()

app.use(pinia)
app.use(router)

app.mount('#app')

最后补充两个小细节

  1. 关于Laravel Sanctum的Cookie:确保你的前端请求已经配置了withCredentials: true,否则getCurrentUser请求不会带上认证Cookie,后端会一直返回未认证状态。比如在axios里配置:
// axios实例
axios.defaults.withCredentials = true
  1. 你之前的Register路由守卫里的.value是多余的:Pinia的store实例会自动解包refcomputed属性,直接用store.isAuthorized就行,不用加.value

这样改完之后,路由守卫就能正确读取到Pinia里的已认证状态了,已登录用户访问/login/register会自动跳转到首页,未登录用户则正常进入~

火山引擎 最新活动