如何使用Vue Router全局导航守卫实现多条件路由鉴权跳转
Vue3+Pinia 路由鉴权(含账号验证状态拦截)实现方案
核心实现思路
由于登录接口仅返回accessToken,账号验证状态需要通过受保护接口获取,因此路由拦截分三层判断:
- 本地无有效token:直接重定向到登录页
- 本地有token但未拉取过用户验证状态:先调用接口获取用户信息,拿到
verified状态后再做跳转判断 - 本地有token且已获取验证状态:根据
verified值判断跳转验证页还是仪表盘
具体实现步骤
第一步:新增Pinia用户状态Store
全局存储token、账号验证状态、用户信息加载标记,避免重复请求接口,保证全应用状态同步。
新建src/stores/user.js文件,代码如下:
import { defineStore } from 'pinia' import axios from 'axios' export const useUserStore = defineStore('user', { state: () => ({ accessToken: localStorage.getItem('accessToken') || '', isVerified: false, // 标记是否已拉取过用户信息,避免路由跳转重复请求接口 userInfoLoaded: false }), actions: { // 拉取用户信息(含账号验证状态,直接复用你现有的dashboard接口即可) async fetchUserInfo() { if (!this.accessToken) return try { const res = await axios.get('https://some-api-url/dashboard', { headers: { Authorization: `Bearer ${this.accessToken}` } }) // 按接口实际返回的字段取verified值 this.isVerified = res.data.data.user.verified this.userInfoLoaded = true } catch (err) { // token失效时清空本地认证状态 if (err.response?.status === 401) { this.clearAuth() } throw err } }, setToken(token) { this.accessToken = token localStorage.setItem('accessToken', token) // 存入新token后重置加载标记,下次路由跳转重新拉取用户信息 this.userInfoLoaded = false this.isVerified = false }, clearAuth() { this.accessToken = '' this.isVerified = false this.userInfoLoaded = false localStorage.removeItem('accessToken') } } })
第二步:调整路由配置+重写全局前置守卫
给路由加差异化meta标记:游客可访问页、仅需登录页、需登录+验证页,守卫里分层判断权限。
修改router/index.js代码如下:
import { createRouter, createWebHistory } from 'vue-router' // 按你的实际文件路径引入userStore import { useUserStore } from '@/stores/user' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: "/signin", name: "signin", component: () => import("../views/SignIn.vue"), meta: { guestOnly: true } // 标记仅游客可访问,已登录用户禁止进入 }, { path: "/verify", name: "verify", component: () => import("../views/Verification.vue"), meta: { needsAuth: true } // 仅需登录,不需要账号验证 }, { path: "/dashboard", name: "dashboard", component: () => import("../views/UserDashboard.vue"), meta: { needsAuth: true, requiresVerified: true } // 需要登录+账号已验证 }, // 根路径默认重定向 { path: '/', redirect: () => { const userStore = useUserStore() if (!userStore.accessToken) return '/signin' if (!userStore.isVerified) return '/verify' return '/dashboard' } } ] }) router.beforeEach(async (to, from, next) => { const userStore = useUserStore() const hasToken = !!userStore.accessToken // 1. 访问需鉴权页面但无token,直接跳登录 if (to.meta.needsAuth && !hasToken) { return next({ name: 'signin' }) } // 2. 已登录用户访问游客专属页(比如登录页),按验证状态跳对应页面 if (to.meta.guestOnly && hasToken) { if (!userStore.userInfoLoaded) { await userStore.fetchUserInfo() } return next(userStore.isVerified ? { name: 'dashboard' } : { name: 'verify' }) } // 3. 有token但未拉取过用户信息,先拉取再判断权限 if (hasToken && !userStore.userInfoLoaded) { try { await userStore.fetchUserInfo() } catch (err) { // 拉取失败(如token过期)已在store中清空状态,直接跳登录 return next({ name: 'signin' }) } } // 4. 访问需验证的页面但账号未验证,跳验证页 if (to.meta.requiresVerified && !userStore.isVerified) { return next({ name: 'verify' }) } // 5. 已验证用户访问验证页,直接跳仪表盘 if (to.name === 'verify' && userStore.isVerified) { return next({ name: 'dashboard' }) } next() }) export default router
第三步:修改登录方法逻辑
登录成功后不要硬编码跳仪表盘,交给路由守卫自动判断跳转目标,同时用store方法存token保证状态同步。
修改loginUser方法代码如下:
// 组件setup中先引入userStore import { useUserStore } from '@/stores/user' const userStore = useUserStore() const loginUser = async () => { try { const res = await axios.post( "https://some-api-url/login", signin.value, { headers: { Accept: "application/json", "Content-Type": "application/json", }, } ); // 调用store方法存token,自动重置用户信息加载状态 userStore.setToken(res.data.data.accessToken); // 跳根路径,由守卫自动判断跳转verify还是dashboard router.push("/"); } catch (error) { const errMsg = error.response?.data?.message || '登录失败,请重试' alert(errMsg); } };
补充规则说明
- 后续新增页面时,仅需登录即可访问的页面加
meta: { needsAuth: true } - 需要账号完成验证才能访问的页面加
meta: { needsAuth: true, requiresVerified: true } - 用户完成账号验证操作后,直接将store中的
isVerified设为true,再调用router.push('/dashboard')即可正常跳转 userInfoLoaded标记会避免每次路由跳转都重复请求用户信息接口,仅在首次加载、token更新时触发拉取
内容的提问来源于stack exchange,提问作者Mena
相关产品推荐
相关产品推荐

