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 做好兜底,体验和可维护性都会提升
相关推荐
weixin_3823952320 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__20 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.20 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~21 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华1 天前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子1 天前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅1 天前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni1 天前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学1 天前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端