Skip to content

动态路由与认证的专业架构设计建议

当前架构的问题

分析您的代码,我发现以下架构设计问题:

  1. 耦合过深:权限获取与路由注册强耦合
  2. 依赖顺序问题:路由初始化→需要认证→但认证状态又需要路由
  3. 状态恢复不完整:持久化数据恢复后没有相应地恢复路由结构
  4. 刷新处理欠缺:浏览器刷新导致运行时路由丢失
  5. 初始化逻辑分散:在不同地方处理相似的初始化逻辑

专业设计建议

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)}`);
  }
});

技术实现关键点

  1. 权限持久化:保存权限数据,不保存路由对象

    typescript
    // 持久化配置
    persist: {
      key: "auth-store",
      storage: localStorage,
      paths: ["permissions", "userInfo", "accessRoutes"]
    }
  2. 路由权限映射:使用映射表而非动态添加

    typescript
    // 将权限数据转换为可访问路由映射
    function generateAccessRoutes(permissions) {
      return allPossibleRoutes.filter(route => {
        return !route.meta.permission || permissions.includes(route.meta.permission);
      });
    }
  3. 页面级权限控制

    vue
    <template>
      <div>
        <button v-if="hasPermission('user:create')">创建用户</button>
        <button v-if="hasPermission('user:edit')">编辑用户</button>
      </div>
    </template>

总结

这个架构变更的核心思想是:将路由定义与权限控制分离,不再使用"动态添加路由"的模式,而是使用"预定义所有路由+权限控制访问"的模式。

这种设计带来的好处:

  1. 刷新页面后无需重新构建路由树
  2. 认证状态和路由状态解耦,更加灵活
  3. 路由结构更加稳定,不受API变化影响
  4. 开发和调试更加便捷,可以清晰看到所有路由
  5. 符合"关注点分离"的软件设计原则

这种架构非常适合中大型应用,能更优雅地处理复杂的权限需求和页面刷新问题。

Released under the MIT License.