动态路由与认证的专业架构设计建议
当前架构的问题
分析您的代码,我发现以下架构设计问题:
- 耦合过深:权限获取与路由注册强耦合
- 依赖顺序问题:路由初始化→需要认证→但认证状态又需要路由
- 状态恢复不完整:持久化数据恢复后没有相应地恢复路由结构
- 刷新处理欠缺:浏览器刷新导致运行时路由丢失
- 初始化逻辑分散:在不同地方处理相似的初始化逻辑
专业设计建议
1. 路由与权限分离模式
前端应用
├── 路由层(所有可能的路由)
├── 权限控制层(决定路由访问权限)
└── 视图层(根据权限显示内容)关键思想:不要动态添加路由,而是预定义所有路由,通过权限控制访问。
2. 具体实现方案
方案A:静态路由 + 权限守卫
typescript
// 1. 预定义所有可能路由(静态定义)
const allRoutes = [...staticRoutes, ...potentialDynamicRoutes];
// 2. 在路由守卫中控制访问权限
router.beforeEach((to, from, next) => {
const authStore = AuthStore();
// 如果是公开路由或登录页,直接通过
if (isPublicRoute(to) || isLoginPage(to)) {
return next();
}
// 检查是否有权限访问该路由
if (authStore.hasPermission(to.name)) {
return next();
}
// 无权限时重定向
next('/403');
});方案B:Meta路由 + 路由映射表
typescript
// 路由元数据模式
const routes = [
{
path: '/dashboard',
component: Layout,
meta: { requiresAuth: true, permission: 'dashboard:view' },
children: [
// 子路由也带有权限控制
]
}
];
// 路由守卫简化为
router.beforeEach((to, from, next) => {
// 检查路由元数据中的权限要求
if (to.meta.requiresAuth && !hasPermission(to.meta.permission)) {
return next('/403');
}
next();
});方案C:角色路由映射模式
typescript
// 1. 定义角色-路由映射
const roleRouteMap = {
admin: ['dashboard', 'user-manage', 'system-config'],
editor: ['dashboard', 'content-manage'],
user: ['dashboard']
};
// 2. 根据用户角色筛选可访问路由
function getAccessibleRoutes(roles) {
const userRoutes = [];
roles.forEach(role => {
const routeNames = roleRouteMap[role] || [];
// 根据routeNames过滤路由...
});
return userRoutes;
}3. 应用启动流程优化
typescript
// main.ts
async function bootstrapApp() {
const app = createApp(App);
app.use(createPinia());
// 1. 安装基础路由(公开路由)
const router = createRouter({
routes: publicRoutes
});
app.use(router);
// 2. 检查认证状态
const authStore = AuthStore();
const hasToken = !!localStorage.getItem('token');
if (hasToken) {
try {
// 3. 验证token有效性并获取用户信息
await authStore.validateToken();
// 4. 根据用户权限生成可访问路由映射
const accessRoutes = generateAccessRoutes(authStore.permissions);
// 5. 应用权限路由映射(不是添加路由,而是激活已有路由的访问权限)
authStore.setAccessRoutes(accessRoutes);
// 6. 导航到目标路由或首页
router.replace(router.currentRoute.value.fullPath || '/');
} catch (error) {
// Token验证失败
authStore.resetAuth();
router.push('/login');
}
}
// 7. 完成挂载
app.mount('#app');
}
bootstrapApp();4. 路由守卫重构
typescript
router.beforeEach(async (to, from, next) => {
NProgress.start();
// 1. 不需要认证的路由直接通过
if (to.meta.public) {
return next();
}
const authStore = AuthStore();
const hasToken = authStore.token;
// 2. 没有Token,重定向到登录页
if (!hasToken) {
return next(`/login?redirect=${encodeURIComponent(to.fullPath)}`);
}
// 3. 已经有权限路由映射,直接通过
if (authStore.hasAccessRoutes) {
return next();
}
try {
// 4. 获取权限并生成路由映射
await authStore.getPermissions();
const accessRoutes = generateAccessRoutes(authStore.permissions);
authStore.setAccessRoutes(accessRoutes);
// 5. 带替换方式导航到目标页面,确保正确的路由记录
return next({ ...to, replace: true });
} catch (error) {
// 6. 权限获取失败,清理认证信息
authStore.resetAuth();
return next(`/login?redirect=${encodeURIComponent(to.fullPath)}`);
}
});技术实现关键点
权限持久化:保存权限数据,不保存路由对象
typescript// 持久化配置 persist: { key: "auth-store", storage: localStorage, paths: ["permissions", "userInfo", "accessRoutes"] }路由权限映射:使用映射表而非动态添加
typescript// 将权限数据转换为可访问路由映射 function generateAccessRoutes(permissions) { return allPossibleRoutes.filter(route => { return !route.meta.permission || permissions.includes(route.meta.permission); }); }页面级权限控制:
vue<template> <div> <button v-if="hasPermission('user:create')">创建用户</button> <button v-if="hasPermission('user:edit')">编辑用户</button> </div> </template>
总结
这个架构变更的核心思想是:将路由定义与权限控制分离,不再使用"动态添加路由"的模式,而是使用"预定义所有路由+权限控制访问"的模式。
这种设计带来的好处:
- 刷新页面后无需重新构建路由树
- 认证状态和路由状态解耦,更加灵活
- 路由结构更加稳定,不受API变化影响
- 开发和调试更加便捷,可以清晰看到所有路由
- 符合"关注点分离"的软件设计原则
这种架构非常适合中大型应用,能更优雅地处理复杂的权限需求和页面刷新问题。
