Vue 3 使用路由守卫进行鉴权
Vue 3 使用路由守卫进行鉴权
1. 引言
为什么需要路由守卫?
在现代 Web 应用中,用户权限管理是一个非常重要的功能。例如,某些页面只允许登录用户访问,而某些页面则需要对用户的角色进行更细粒度的控制。为了实现这些功能,我们需要在用户访问特定页面之前进行鉴权。
Vue Router 提供了路由守卫(Route Guards)功能,允许我们在路由跳转之前、之后或解析过程中执行自定义逻辑。通过路由守卫,我们可以轻松实现鉴权功能,确保只有授权用户才能访问特定页面。
路由守卫的应用场景
- 登录验证:确保用户已登录后才能访问受保护的页面。
- 角色权限控制:根据用户的角色限制其访问权限。
- 数据预加载:在进入某个页面之前预加载所需数据。
- 页面访问日志:记录用户的页面访问行为。
2. Vue 3 和 Vue Router 简介
Vue 3 的核心特性
Vue 3 是 Vue.js 的最新版本,带来了许多新特性:
- Composition API:更灵活的逻辑复用方式。
- 更好的 TypeScript 支持:Vue 3 完全使用 TypeScript 重写。
- 性能优化:更快的渲染速度和更小的包体积。
Vue Router 的基本用法
Vue Router 是 Vue.js 的官方路由管理器,用于构建单页面应用(SPA)。以下是一个简单的 Vue Router 配置示例:
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
3. 路由守卫的类型
全局前置守卫 (beforeEach
)
在路由跳转之前执行,常用于鉴权。
router.beforeEach((to, from, next) => {
// 鉴权逻辑
next();
});
全局解析守卫 (beforeResolve
)
在路由被解析之前执行,适用于需要等待异步数据加载的场景。
router.beforeResolve((to, from, next) => {
// 数据预加载逻辑
next();
});
全局后置守卫 (afterEach
)
在路由跳转之后执行,适用于页面访问日志记录等场景。
router.afterEach((to, from) => {
// 页面访问日志记录
});
路由独享守卫 (beforeEnter
)
在某个特定路由配置中定义,仅对该路由生效。
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/Admin.vue'),
beforeEnter: (to, from, next) => {
// 鉴权逻辑
next();
},
}
组件内守卫 (beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
)
在组件内定义,控制组件的访问和离开。
export default {
beforeRouteEnter(to, from, next) {
// 组件进入前的逻辑
next();
},
beforeRouteUpdate(to, from, next) {
// 组件更新时的逻辑
next();
},
beforeRouteLeave(to, from, next) {
// 组件离开时的逻辑
next();
},
};
4. 实现鉴权的基本思路
什么是鉴权?
鉴权是指验证用户是否有权限访问某个资源。在 Web 应用中,通常通过以下方式实现鉴权:
- 登录验证:检查用户是否已登录。
- 角色权限控制:根据用户的角色限制其访问权限。
鉴权的常见方式
- 前端鉴权:通过路由守卫控制页面访问权限。
- 后端鉴权:通过 API 接口验证用户权限。
5. 实战:使用路由守卫进行鉴权
项目初始化
使用 Vue CLI 或 Vite 创建一个新的 Vue 3 项目:
npm create vite@latest my-vue-app --template vue-ts
cd my-vue-app
npm install
定义路由和元信息
在 router/index.ts
中定义路由,并为需要鉴权的路由添加 meta
字段。
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
meta: { requiresAuth: false },
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { requiresAuth: true },
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue'),
meta: { requiresAuth: false },
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
实现全局前置守卫
在 router/index.ts
中使用 beforeEach
钩子进行鉴权。
import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
router.beforeEach(
(
to: RouteLocationNormalized,
from: RouteLocationNormalized,
next: NavigationGuardNext
) => {
const isAuthenticated = checkAuth(); // 检查用户是否登录
const requiresAuth = to.meta.requiresAuth; // 判断目标路由是否需要鉴权
if (requiresAuth && !isAuthenticated) {
// 如果需要鉴权且用户未登录,跳转到登录页
next({ name: 'Login' });
} else if (to.name === 'Login' && isAuthenticated) {
// 如果用户已登录但尝试访问登录页,跳转到首页
next({ name: 'Home' });
} else {
// 其他情况,允许访问
next();
}
}
);
// 模拟检查用户是否登录的函数
function checkAuth(): boolean {
return !!localStorage.getItem('token');
}
结合 Pinia 或 Vuex 进行状态管理
如果使用 Pinia 或 Vuex 管理用户登录状态,可以在路由守卫中从 Store 中获取登录状态。
Pinia 示例
import { useAuthStore } from '@/stores/auth';
router.beforeEach((to, from, next) => {
const authStore = useAuthStore();
const requiresAuth = to.meta.requiresAuth;
if (requiresAuth && !authStore.isAuthenticated) {
next({ name: 'Login' });
} else if (to.name === 'Login' && authStore.isAuthenticated) {
next({ name: 'Home' });
} else {
next();
}
});
Vuex 示例
import store from '@/store';
router.beforeEach((to, from, next) => {
const requiresAuth = to.meta.requiresAuth;
const isAuthenticated = store.getters.isAuthenticated;
if (requiresAuth && !isAuthenticated) {
next({ name: 'Login' });
} else if (to.name === 'Login' && isAuthenticated) {
next({ name: 'Home' });
} else {
next();
}
});
处理登录和登出逻辑
在登录和登出时更新用户的登录状态。
// 登录成功后保存 token
localStorage.setItem('token', 'your-auth-token');
router.push({ name: 'Dashboard' });
// 登出时清除 token
localStorage.removeItem('token');
router.push({ name: 'Login' });
6. 进阶:动态路由和权限控制
动态路由的实现
动态路由是指根据用户权限动态生成路由表。例如,管理员和普通用户看到的路由可能不同。
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
},
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/Admin.vue'),
meta: { requiresAuth: true, roles: ['admin'] },
},
{
path: '/user',
name: 'User',
component: () => import('@/views/User.vue'),
meta: { requiresAuth: true, roles: ['user'] },
},
];
在路由守卫中根据用户角色动态生成路由:
router.beforeEach((to, from, next) => {
const userRole = getUserRole(); // 获取用户角色
const requiredRoles = to.meta.roles; // 获取目标路由所需的角色
if (requiredRoles && !requiredRoles.includes(userRole)) {
next({ name: 'Forbidden' }); // 如果用户角色不符合要求,跳转到无权限页面
} else {
next();
}
});
基于角色的权限控制
通过 meta
字段定义路由所需的角色,并在路由守卫中进行验证。
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/Admin.vue'),
meta: { requiresAuth: true, roles: ['admin'] },
}
7. 常见问题与解决方案
路由守卫的死循环问题
如果路由守卫逻辑不正确,可能会导致死循环。例如,未登录用户尝试访问受保护页面时,被重定向到登录页,而登录页又重定向到其他页面。
解决方案:确保在路由守卫中正确处理重定向逻辑。
如何处理未定义的路由?
如果用户访问了一个未定义的路由,可以跳转到 404 页面。
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/NotFound.vue'),
}
路由守卫的性能优化
如果路由守卫逻辑复杂,可能会影响页面加载性能。
解决方案:将复杂的逻辑拆分为异步函数,或使用缓存机制。
8. 总结与展望
路由守卫的最佳实践
- 明确鉴权逻辑:确保路由守卫的逻辑清晰易懂。
- 避免死循环:正确处理重定向逻辑。
- 结合状态管理:使用 Pinia 或 Vuex 管理用户登录状态。
未来发展方向
- 更细粒度的权限控制:支持更复杂的权限模型。
- 动态路由生成:根据用户权限动态生成路由表。
通过本文的学习,你应该已经掌握了如何在 Vue 3 中使用路由守卫进行鉴权。希望这些内容能帮助你在实际项目中更好地实现用户权限管理功能!