☰
若依权限体系:GetInfo与GenerateRouters动态路由
2026/10/1 1:37:01 网站建设 项目流程

1. 登录之后到底发生了什么:GetInfo 与 GenerateRouters 的整体设计

很多人第一次接触若依前后端分离版本,会觉得登录成功之后就“自动”有了菜单和按钮权限,好像框架有魔法一样。实际上,这背后是一条非常清晰的链路:登录接口只负责发放 token,真正决定你看到什么菜单、能点哪些按钮的,是紧接着调用的两个接口——GetInfo和GenerateRouters。搞懂这两个接口,基本就搞懂了若依权限体系的半壁江山。

我在好几个基于若依二次开发的项目里踩过坑,最常见的场景就是:新来的同事把菜单配好了,角色也分配了,但前端死活不显示;或者按钮权限明明加了v-hasPermi,却怎么都拦不住。归根结底,都是因为对这两个接口返回的数据结构、生成逻辑没有吃透。这篇文章我会把GetInfo获取用户角色和权限、GenerateRouters获取动态路由、以及首页数据加载这三块内容从头到尾拆一遍,既讲清楚“为什么这么设计”,也给出可以直接抄的排查方法和实操建议。

这套机制适合所有正在使用若依前后端分离版本、或者准备基于它做二次开发的人。不管你是刚接手项目的新手,还是已经改过几版权限逻辑的老手,把这条链路理顺之后,很多玄学问题都会变得有迹可循。

2. GetInfo 接口深度拆解:角色和权限究竟怎么查出来的

2.1 接口入口与调用时机

GetInfo对应的后端入口在SysLoginController里,路径通常是/getInfo。它的调用时机很关键:前端在登录拿到 token 之后,会立刻带着这个 token 请求GetInfo。也就是说,token 是敲门砖,GetInfo才是真正把用户身份信息交给前端的接口。

这个接口返回的数据大致长这样:

{ "code": 200, "user": { "userId": 1, "userName": "admin", "nickName": "若依", "avatar": "", "roles": ["admin"], "permissions": ["*:*:*"] }, "roles": ["admin"], "permissions": ["*:*:*"] }

注意这里有一个容易被忽略的点:user对象里也带了roles和permissions,外层又重复了一份。这不是冗余设计失误,而是历史兼容和前端不同取值习惯共同造成的。前端 store 里通常取外层的roles和permissions,但有些自定义页面会直接从user对象里读。你自己写代码时,建议统一取外层,避免两边不一致时排查困难。

2.2 权限标识是怎么被收集出来的

GetInfo内部做的事情,核心其实就两步:查角色、查权限。角色通过用户 ID 去用户角色关联表里查,权限则通过角色去菜单表里查对应的perms字段。

我用一个更直白的方式解释这个过程。假设你给用户分配了“运营人员”这个角色,这个角色关联了“文章管理”目录、“文章列表”菜单、“新增文章”按钮。那么:

  • 角色查询会得到["operator"]这样的角色标识集合。
  • 权限查询会把“文章列表”菜单的perms(比如article:list)和“新增文章”按钮的perms(比如article:add)全部拉出来,组成权限集合。

这里有个细节值得说明:目录类型菜单的perms一般是空的,因为它只负责分组,不承载具体操作权限。真正产生权限标识的是菜单类型和按钮类型。如果你发现某个权限怎么都不生效,先去菜单管理里看看那条记录的perms是不是空的,或者是不是配成了目录。

2.3 超级管理员的特殊处理

若依对超级管理员做了硬编码处理。用户 ID 为 1 的 admin 用户,返回的权限集合是["*:*:*"],角色是["admin"]。这个*:*:*是一个通配符,前端在判断权限时会特殊放行。

我在实际项目里遇到过有人把 admin 的用户 ID 改了,结果权限全失效。这个 ID 判断是写死在代码里的,不是通过角色标识判断的。如果你要做多管理员或者改超级管理员定义,记得同步改这块逻辑,否则会非常隐蔽地出问题。另外,有些团队会在生产环境禁用 admin 账号,改用普通账号加最高权限角色,这时候那个*:*:*通配逻辑就用不上了,所有权限都得老老实实配置。

2.4 返回数据背后的缓存与性能考量

GetInfo每次登录都会查一次数据库。对于菜单和角色数据变动不频繁的系统,这个开销可以接受。但如果你的系统菜单特别多,或者有大量按钮级权限,每次查全量权限集合会有一定压力。

我试过的一个优化方案是:把角色的权限集合做一层本地缓存,菜单或角色变更时主动失效。不过要注意,缓存失效做不干净会导致权限改了不生效,这比性能问题更让人头疼。如果你的系统规模不大,我建议先不要加缓存,等真正出现性能瓶颈再说。对于绝大多数中小型项目,GetInfo的查询耗时都在可接受范围内。

提示:GetInfo里的权限集合是“当前用户所有角色权限的并集”。如果一个人有多个角色,权限会合并去重。排查权限问题时,先确认是不是多个角色叠加导致的预期外放行。

3. GenerateRouters 动态路由:后端怎么拼出前端菜单树

3.1 从菜单表到路由对象的转换逻辑

GenerateRouters是若依动态路由的核心。它的任务是把数据库里的菜单记录,转换成前端 Vue Router 能识别的路由配置。这个转换过程主要在SysMenuServiceImpl的buildMenus方法里完成。

一条菜单记录转换成路由对象时,大致会映射这些字段:

  • path来自菜单的path字段。
  • name来自菜单的routeName(路由名称,需要唯一)。
  • component来自菜单的component字段,目录类型通常是Layout,菜单类型是具体页面路径。
  • meta里包含title、icon、noCache、link等信息。
  • children是递归构建的子路由。

这里最容易出问题的是component字段。目录必须用Layout,菜单必须用实际的组件路径(比如system/user/index),如果配反了,轻则页面白屏,重则整个路由树挂掉。我在项目里见过有人把菜单的component写成了Layout,结果点进去永远是个空壳。

3.2 目录、菜单、按钮三种类型的差异化处理

若依菜单表里有个menuType字段,M 代表目录,C 代表菜单,F 代表按钮。buildMenus在处理时逻辑完全不同:

类型是否生成路由是否进菜单树典型 component
目录 M是是Layout
菜单 C是是具体页面组件
按钮 F否否无

按钮类型不会出现在路由里,它只在GetInfo的权限集合里体现,配合前端的v-hasPermi指令做按钮级控制。这就是为什么你新增一个按钮菜单后,菜单树没变化,但按钮权限生效了——它走的是另一条路。

3.3 外链、内嵌与缓存路由的处理

除了普通路由,若依还支持几种特殊场景:

  • 外链:如果菜单的isFrame为 0 或者path是完整 URL,会生成一个特殊路由,点击后在浏览器新标签打开。实现上通常是通过InnerLink组件包一层 iframe。
  • 内嵌:在系统内部以 iframe 形式打开外部页面,菜单仍然在当前系统框架内。
  • 缓存控制:meta.noCache决定页面是否被 keep-alive 缓存。默认菜单是不缓存的,如果要缓存,需要在菜单管理里设置“是否缓存”为缓存。

我在实操中经常提醒团队:外链的path如果是完整 URL,前端的路由匹配要格外小心,因为它和后端生成的component组合方式不同于普通菜单。配错了不会报错,只会默默打不开。

3.4 前端如何消费这些路由

后端返回路由数组后,前端的处理链路是这样的:

  1. permission.js里的路由守卫在进入页面前拦截。
  2. 如果 store 里还没有路由,就调用GenerateRouters接口。
  3. 拿到路由数据后,通过router.addRoute动态注册。
  4. 同时把路由数据存入 Vuex/Pinia,供侧边栏渲染使用。

这里有个经典坑:动态路由添加后,必须确保next({ ...to, replace: true })重新触发一次导航,否则会出现刷新后白屏或者 404。因为第一次导航时路由还没注册完,直接放行会匹配不到。

// permission.js 里的关键片段 const accessRoutes = await store.dispatch('permission/generateRoutes', roles) accessRoutes.forEach(route => { router.addRoute(route) }) next({ ...to, replace: true })

这段代码里replace: true很重要,它避免在浏览器历史里留下一条无效记录。我见过有人漏了这句,用户点后退会回到一个空白页,体验很差。

4. 首页数据加载:从路由就绪到内容渲染的完整链路

4.1 首页组件挂载后的请求时机

动态路由注册完成、用户进入首页之后,首页组件(通常是index.vue)开始挂载。这时候才轮到首页数据加载登场。它的时机在动态路由之后,因为首页本身也是动态路由的一部分,路由没就绪,组件根本不会渲染。

首页数据加载一般分两类:一类是框架自带的统计卡片、版本信息等;另一类是业务自定义的看板数据。框架自带的部分通常写死在首页组件里,业务看板则需要你自己在created或mounted里发起请求。

我建议的做法是:把首页数据请求统一放在mounted里,并用Promise.all并行发起,避免多个请求串行导致的加载缓慢。如果某个数据块加载失败,用错误边界包起来,不要让整页崩掉。

4.2 常见首页数据源的接入方式

我在几个项目里接过的首页数据源主要有这几类:

  • 统计接口:返回用户数、订单数、文章数等汇总值。通常单独提供一个/dashboard/stats之类的接口。
  • 图表数据:折线图、柱状图的数据集合,一般和图表库配合使用。
  • 通知公告:从公告表里拉最新几条,展示在首页角落。
  • 待办事项:根据当前用户角色过滤出的待处理任务。

这里有个实操心得:首页接口不要和GetInfo混在一起。有人图省事,把统计数字塞进GetInfo的返回里,结果导致登录变慢,而且缓存和权限逻辑搅在一起,后面很难维护。首页数据该独立就独立。

4.3 首页加载与权限的联动

首页上如果也有按钮级操作,同样受GetInfo返回的权限集合控制。比如“导出报表”按钮,如果当前用户没有dashboard:export权限,就应该隐藏。这部分的判断和普通页面完全一致,用v-hasPermi即可。

需要注意的是,首页的权限判断发生在数据加载之前还是之后,会影响用户体验。如果先加载数据再判断权限,用户可能看到一瞬间的按钮闪现。我的做法是先用权限指令控制渲染,数据请求照常发起,两者互不阻塞。

5. 常见问题与排查技巧实录

5.1 菜单不显示、路由丢失的排查路径

这是最高频的问题。我的排查顺序通常是:

  1. 先看GetInfo返回的角色和权限是否正确。如果角色为空,说明用户角色关联没配好。
  2. 再看GenerateRouters返回的路由数组是否为空。为空通常是菜单没有分配给角色,或者菜单状态是停用。
  3. 检查菜单的component字段。目录必须是Layout,菜单必须是真实组件路径。
  4. 检查路由name是否重复。重复的name会导致后注册的覆盖先注册的。
  5. 检查前端控制台有没有addRoute相关的警告。

我整理了一个速查表:

现象可能原因快速验证
菜单完全为空角色未分配菜单查角色菜单关联表
某个菜单缺失菜单状态停用或隐藏查菜单 visible/status
点击菜单白屏component 路径错误比对组件实际路径
刷新后 404动态路由未重新注册查 permission.js 逻辑
按钮权限失效perms 字段为空或拼写错误查菜单 perms 字段

5.2 权限标识不生效的典型原因

按钮权限不生效,八成是这几个原因之一:perms字段拼写和前端v-hasPermi里的字符串不一致;权限集合被多个角色叠加后没有正确去重;或者前端缓存了旧的权限数据,退出重登才生效。

我踩过最深的一个坑是:修改了角色的权限后,当前在线用户的权限不会实时刷新。因为权限是在登录时一次性拉取的。如果业务要求实时生效,需要额外的刷新机制,比如提供手动刷新权限的入口,或者用 websocket 推送变更。大多数项目其实不需要这么复杂,重新登录即可。

5.3 首页白屏与数据不加载的处理

首页白屏通常不是首页本身的问题,而是动态路由链路出了问题。我的经验是:先确认路由是否注册成功,再看首页组件是否被正确匹配。可以在permission.js里加日志,打印出最终注册的路由列表。

如果首页能显示但数据不加载,检查请求是否被权限拦截、接口路径是否正确、以及是否有跨域问题。前后端分离部署时,跨域和网关转发是高频故障点。

注意:动态路由的调试建议在开发环境打开 Vue DevTools 的路由面板,能直观看到注册了哪些路由。生产环境排查则主要靠后端接口返回和前端控制台。

6. 我在实际项目里的一些体会

这套 GetInfo 加 GenerateRouters 的组合,用顺了之后其实非常省心。我个人的体会是,二次开发时尽量不要去动这两个接口的核心结构,而是通过扩展菜单字段、增加自定义权限标识来满足需求。一旦改了返回结构,前端 store、路由守卫、权限指令都得跟着改,牵一发动全身。

另外一个小技巧:如果你需要给某些页面加“仅特定角色可见”的逻辑,除了用权限标识,还可以在路由meta里塞自定义角色字段,在路由守卫里统一判断。这样比在每个页面里写判断要清爽得多。首页数据加载这块,我倾向于把请求封装成独立的 service 层,首页组件只负责调用和渲染,后续要换数据源或者加缓存都方便。

最后再提一句,若依的这套机制本质上就是“登录拿 token,token 换身份,身份换路由,路由驱动页面”。把这四步的每一步都验证清楚,任何权限和菜单问题都能顺藤摸瓜找到根因。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询