Vue Router 权限路由:动态路由、导航守卫与白名单的工程落地

Vue Router 权限路由:动态路由、导航守卫与白名单的工程落地

后台管理系统最常见的"前端安全"问题不是加密,而是权限:

  • 登录后菜单如何按角色显示?
  • 直接输入 URL 能不能越权?
  • 刷新页面后动态路由丢失怎么办?

这篇给你一套能直接落地的权限路由模型:

  • 白名单:登录页/公共页直接放行
  • 登录态校验:无 token 一律回登录(带 redirect)
  • 动态路由注入:根据角色/菜单生成可访问路由
  • 刷新重建:刷新后重新拉用户信息并重新注入
  • 越权兜底:401/403/404 的处理一致且可解释

重点不是背 API,而是把"权限"做成一条可观测、可排查、可迭代的工程链路。


1. 先把权限模型说清楚(否则一定返工)

权限通常分三层,越往下越"硬":

  • 展示权限:菜单/按钮显隐
  • 访问权限:路由能不能进
  • 数据权限:接口返回什么数据(这层必须由后端保证)

这篇聚焦前两层:展示 + 访问。

一个重要结论:

  • 菜单只是 UI,路由守卫必须兜底访问权限

2. 你需要哪些基础能力

要做成可维护的权限路由,至少要有这些"权威信息源":

  • 登录态:token
  • 当前用户:userInfo(至少包含 role/permissions)
  • 路由来源:
    • 前端静态路由(公共页、基础布局)
    • 动态路由(按角色生成/按菜单生成)

以及一个"防重复注入"的标记:

  • isRoutesReady(动态路由是否已注入)

3. 基础结构:白名单 + 受保护路由

白名单路由(无需登录):

  • /login
  • /404

其它路由默认需要登录。


4. 导航守卫主流程(推荐:覆盖 90% 后台项目)

你可以用这个主流程覆盖 90% 项目:

  1. 未登录访问受保护路由 -> 跳登录(带 redirect)
  2. 已登录但用户信息未加载 -> 拉取用户信息
  3. 根据角色生成动态路由 -> 注入 router
  4. 再次进入目标路由(replace 避免重复历史)
  5. 访问无权限 -> 跳 403/404

伪代码(核心是"登录态校验 -> 用户信息 -> 注入动态路由 -> replace 继续前进"):

js 复制代码
router.beforeEach(async (to, from, next) => {
  const userStore = useUserStore()
  const permissionStore = usePermissionStore()

  const whiteList = ['/login', '/404']
  if (whiteList.includes(to.path)) return next()

  if (!userStore.token) {
    return next(`/login?redirect=${encodeURIComponent(to.fullPath)}`)
  }

  // 刷新后:token 在但内存 userInfo 丢了 -> 重新拉
  if (!userStore.userInfo) await userStore.fetchUserInfo()

  // 动态路由未就绪 -> 生成并注入,然后 replace 继续
  if (!permissionStore.isRoutesReady) {
    const dynamicRoutes = buildRoutesByRole(userStore.userInfo.role)
    dynamicRoutes.forEach((r) => router.addRoute(r))
    permissionStore.isRoutesReady = true
    return next({ ...to, replace: true })
  }

  if (!hasRoutePermission(to, userStore.userInfo.role)) {
    return next('/404')
  }

  next()
})

关键点:

  • replace: true 避免"注入路由后同一路由进两次"
  • 动态路由注入要在刷新后能重新执行

工程上的底线:

  • 守卫里不要发重复请求 :用 isRoutesReady 或类似标记保证只初始化一次
  • 不要用菜单显隐代替路由权限:UI 可以绕过,守卫不能

5. 动态路由怎么生成更好维护(两种路线)

常见两种策略:

5.1 前端写死路由表 + 按角色过滤

优点:

  • 简单、可控
  • 不依赖后端返回路由配置

适合中小型项目。

5.2 后端返回菜单/路由配置 + 前端映射组件

优点:

  • 菜单可配置
  • 多系统统一权限模型

风险:

  • 需要维护"组件映射表"
  • 后端字段变化会导致前端路由失效

工程建议:

  • 如果你是个人/小团队项目,优先 前端静态路由表 + 过滤,可控、可读、可测试
  • 如果你是多系统统一平台,再考虑 后端菜单配置,但要把"组件映射表 + 回滚策略"做扎实

6. 刷新后动态路由丢失怎么解决(必考点)

本质原因:

  • 浏览器刷新后,内存中的 router 动态注入状态会丢

解决策略:

  • 刷新后在守卫里重新拉取用户信息重新注入动态路由
  • token 持久化(localStorage)
  • userInfo 可以不持久化,刷新后重新请求更安全

一个更稳的实现方式:

  • token 持久化
  • userInfo 不强依赖持久化
  • 每次刷新进入守卫:
    • token 有 -> fetchUserInfo()(如果 userInfo 缺失)
    • routes 未注入 -> build + addRoute + replace: true

7. 越权与异常兜底:401/403/404 怎么选

建议兜底路径:

  • 401:登录失效 -> 回登录
  • 403:无权限 -> 403 页面(或 404 隐藏资源)
  • 404:路由不存在 -> 404 页面

如果你不想暴露资源存在性,可以把无权限也跳 404。

常用选择:

  • ToC 或对外平台:倾向把无权限也跳 404(隐藏资源存在性)
  • 内部后台:可以用 403 页面(更利于排查权限配置)

注意:

  • 401 通常由 Axios 拦截器统一处理(清理登录态并跳登录),路由守卫只做补兜底。

8. 常见坑与排查

  • 刷新白屏/404 :大概率是动态路由未注入就进入了目标路由
    • 解决:守卫里注入后 next({ ...to, replace: true })
  • 无限重定向:登录页没加入白名单,或守卫里无条件跳转
  • 菜单有但进不去:展示权限和访问权限未统一(菜单过滤了,路由守卫没放行 / 或相反)
  • 切换账号后菜单没刷新 :退出登录时没清理 isRoutesReady 与动态路由

9. 面试表达(30 秒讲清楚)

我一般会这样讲:

  • 我把权限分成展示权限和访问权限,访问权限由路由守卫兜底。
  • 守卫流程是:白名单放行 -> 无 token 回登录(带 redirect)-> 有 token 但 userInfo 缺失就拉用户信息 -> 根据角色生成动态路由并注入 -> replace 继续进入目标路由。
  • 刷新后动态路由会丢,所以守卫里要做重建,并用标记避免重复注入。
  • 无权限我会根据场景选择 403 或 404,并且 401 一般交给 Axios 拦截器统一处理。

10. 总结

  • 权限路由要保证"访问权限",不是只做菜单显示
  • 导航守卫:登录态校验 -> 拉用户信息 -> 注入动态路由 -> replace 进入目标路由
  • 刷新后路由丢失是正常现象,靠守卫重建即可
  • 401/403/404 做好兜底,体验和可维护性都会提升
相关推荐
霍理迪2 小时前
Vue—其他指令及自定义指令
前端·javascript·vue.js
小江的记录本2 小时前
【Filter / Interceptor】过滤器(Filter)与拦截器(Interceptor)全方位对比解析(附底层原理 + 核心对比表)
java·前端·后端·spring·java-ee·前端框架·web
独泪了无痕2 小时前
Vue3动态组件Component的深度解析与应用
前端·vue.js·web components
lbh10 小时前
当我开始像写代码一样和AI对话,一切都变了
前端·openai·ai编程
We་ct10 小时前
LeetCode 918. 环形子数组的最大和:两种解法详解
前端·数据结构·算法·leetcode·typescript·动态规划·取反
qq_4061761411 小时前
深入浅出 Pinia:Vue3 时代的状态管理新选择
javascript·vue.js·ecmascript
wefly201711 小时前
m3u8live.cn 在线M3U8播放器,免安装高效验流排错
前端·后端·python·音视频·前端开发工具
C澒12 小时前
微前端容器标准化 —— 公共能力篇:通用打印
前端·架构
德育处主任Pro12 小时前
前端元素转图片,dom-to-image-more入门教程
前端·javascript·vue.js