RBAC 权限模型实战:从 ACL 踩坑到 RBAC3 落地(2026) RBAC 权限模型实战从 ACL 踩坑到 RBAC3 落地2026今年我接手过一个后台开发权限是用 ACL 写的——每个用户挂一串资源操作。上线两周甲方提了个需求「把某些区的销售都加上订单导出」。我打开配置一看47 个人每人改一遍。改完我坐那想这不对劲。后来我把权限层换成了 RBAC 权限模型同样的需求改一行就完事。这篇文章是我那一次重构的复盘也把 RBAC 的几个层次一次讲清楚。点个收藏我们开始。一、先回答一个问题RBAC 到底解决什么很多人会回答「管权限」。这个答案放在面试里能过放在工程里会出事。我自己的结论是RBAC 权限模型解决的不是「有没有权限」的问题而是「权限变动成本」的问题。把权限直接绑到用户身上ACL 模型加一个人、改一个人、撤一个人都是 O(1) 的但「批量改一类人」是 O(N) 的而且容易漏。把权限绑到角色上、人只挂角色那么批量改一类人就是改一次角色N 个人同时生效。这就是 RBAC 的核心收益。ACLAccess Control List访问控制列表一种把权限直接挂到具体资源上的老模型每条规则形如「资源 X 允许用户 Y 做 Z」。你可以理解为「门上贴名单谁进谁在名单上」。RBACRole-Based Access Control基于角色的访问控制把权限绑到角色上用户只和角色挂钩角色再拥有权限。你可以理解为「门上贴工牌级别人凭工牌进」。我画一张图你看完就知道这两种差别有多大左边的 ACL 写法张三李四各自挂一串权限运维改起来要一条条点。右边的 RBAC 写法张三只挂「运营」这个角色李四挂「主管」权限都在角色上。改运营能干啥所有运营一次性跟着变。可能有人会问那 ACL 是不是就该淘汰了不是。ACL 适合「资源特别碎、每个资源的 owner 都不一样」的场景比如 Linux 文件的权限位、对象存储桶里的单条授权。RBAC 适合「角色相对固定、人员流动频繁」的中后台业务。两者不是替代是分工。二、RBAC 权限模型的三件套用户、角色、权限RBAC 的最小可用形态叫 RBAC0就三个东西用户User、角色Role、权限Permission。权限绑资源操作比如order:read、order:write、report:export。三件事的关系是两组多对多用户和角色多对多一个人可以兼多个角色角色和权限多对多一个角色可以拥有一组权限。落地到表就是user、role、permission、user_role、role_permission五张表足够你跑通 80% 的中后台。我自己的代码里权限不直接写成读订单而是拆成resourceaction两列。这样后面前端要按资源过滤、按操作过滤都好做。比如resourceorder, actionexport比一个字符串order:export灵活得多。三、RBAC0 到 RBAC3什么时候需要哪一档这是面试和实战都最容易答含糊的地方。我先给一张演进图再逐个说人话。RBAC0最基础的三件套能跑通用户-角色-权限。一个小公司内部系统用 RBAC0 完全够。RBAC1在 RBAC0 上加了角色继承。高级经理自动继承经理的权限经理继承员工的权限不用重复定义。RBAC2在 RBAC0 上加了约束。比如「财务和审计不能是同一个人」「一个项目只能有一个经理」。RBAC3RBAC1 RBAC2既要继承又要约束大型企业的方案。我给你列个对比表怎么选一目了然模型关键能力适用场景落地成本RBAC0用户-角色-权限三元组中小后台、人员 100低5 张表RBAC1角色继承层次有清晰职级体系的公司中加角色自引用RBAC2互斥/先决/基数约束金融、审计、合规要求高高需要约束引擎RBAC3继承 约束全要大型企业集团高需要完整权限中台我见过不少团队一上来就要 RBAC3结果约束规则写到一半发现业务自己也说不清先决条件是啥。我的建议是从 RBAC0 起步等业务真的被某一类问题反复折磨了再加对应的能力。别为了显得专业提前上复杂度。四、角色继承别让高级经理重复造轮子RBAC1 解决的是「重复定义」的痛。我举个我吃过的亏最早我把「经理」和「高级经理」当两个独立角色各自挂一坨权限。后来公司要改所有员工都能看公司公告——我得改 3 个角色。如果当时用了继承改员工一个角色就够了。继承是传递的高级经理继承经理、经理继承员工那么高级经理自动拥有员工的所有权限。在表设计上role表加一列parent_id指向父角色就行查权限时沿父链向上递归合并。但要提醒一点继承不要太深。我见过一家公司角色继承链 5 层结果权限算下来谁也说不清高级总监到底能不能导出报表。我自己定的规矩是继承层级不超过 3 层再深就拆角色或者用组合。可能有人会问继承和直接把多个角色都挂给一个人有啥区别区别在「变动成本」。给一个人挂 3 个角色是 N 次操作给一个角色继承另一个角色是 1 次操作、影响所有挂这个角色的人。当你需要一类人统一加权限时继承是免费的红利。五、约束为什么审计和财务不能是同一个人RBAC2 加的是「规则」。最常用的三种约束我直接给结论互斥角色Mutual Exclusion某些角色不能同时属于一个人。最经典就是财务 审计——一个人既录付款又复核付款账就有动手脚的空间。先决角色Prerequisite要拿 B 角色必须先有 A 角色。基数约束Cardinality一个角色最多给 N 个人或一个人最多挂 N 个角色。互斥约束的代码实现不复杂加一张role_constraint表存「角色 A 与角色 B 互斥」一条规则即可分配角色前查一下冲突表。我自己在金融项目里就用这一招过合规审计审计员看了直点头。但要注意一个坑互斥要在「分配时」和「查询时」都校验。只在分配时挡住不够历史数据可能已经违规要定期跑一个对账任务把同时持有互斥角色的人捞出来。六、用户组批量加人的救星到这一步你已经能跑 RBAC3 了但还会遇到一个恶心场景销售部 30 个人都要挂销售员角色。新来一个销售HR 还得记得给他挂角色HR 忘了新人就啥都干不了。用户组User Group不是 RBAC 标准里的概念但几乎所有落地 RBAC 的中台都加了。它把「一群同质用户」聚合起来对组分配角色组内成员自动继承。生活化比喻用户组就是「HR 拉了个工作群」群里的人自动有群的权限HR 只要管「谁进群」不用管「群里发了什么角色」。表结构上多两张user_group、user_group_member再加一张group_role把组和角色关联起来。员工入组后权限计算时把组角色一并合并进「该用户的生效角色集」。新人入离职就是 group member 的一行 INSERT/DELETE权限完全不用动。七、四种权限粒度菜单/操作/数据/字段到这里 RBAC 的骨架就齐了但很多团队上线后发现还差一口气——权限怎么落不到按钮上这是「权限粒度」的问题。我把实战中常用的四种粒度列出来菜单权限控制后台左侧菜单和页面是否可见。粒度粗挡误入但挡不住接口直接调。操作权限控制按钮新增/删除/导出和后端接口是否可调。这是真正卡口的地方必须在后端校验前端只是 UX。数据权限控制能看到哪些范围的数据——本部门、本城市、全公司、自定义。这块最容易漏设计很多人到上线才发现销售 A 能看到销售 B 的客户。字段权限控制字段是否可见/可编辑。比如薪资字段对 HR 可见、对其他员工不可见。我个人的落地顺序是操作权限 数据权限 菜单权限 字段权限。操作权限是底线不校验等于裸奔数据权限是业务正确性菜单权限是体验字段权限是锦上添花。数据权限的实现我推荐用「规则表达式 查询改写」在角色上挂一个数据范围规则比如dept_id ${user.dept_id}然后在查询拦截器里自动把这个条件 AND 进 SQL。这样业务代码不感知新规则加进来零侵入。八、落地到表一份能跑的 ER 设计讲了这么多模型最后给一份我项目里在用的表结构你拿去改改就能用。核心 8 张表user用户主表role角色表含parent_id继承用permission权限表拆resourceactionuser_role用户-角色多对多role_permission角色-权限多对多user_group用户组表user_group_member组-用户多对多group_role组-角色多对多加约束的话再补role_constraint存互斥/先决规则和role_inherit如果继承关系是多父角色就用独立表单父用parent_id就够。权限计算的核心是「拿一个用户的所有生效角色」先查user_role再查用户所在的所有user_group_member对应的group_role最后沿role.parent_id向上递归合并父角色。把这一坨角色 id 收齐后再role_permissionjoin 出权限集。一段示例 SQL给你看「某用户所有生效权限」怎么算MySQL 8用 CTE 递归-- 示例查用户 1001 的所有生效权限WITHRECURSIVE user_rolesAS(-- 直接挂的角色SELECTrole_idFROMuser_roleWHEREuser_id1001UNION-- 通过用户组挂的角色SELECTgr.role_idFROMuser_group_member gmJOINgroup_role grONgr.group_idgm.group_idWHEREgm.user_id1001),role_chainAS(-- 沿继承链向上SELECTrole_idFROMuser_rolesUNIONSELECTr.parent_idFROMrole_chain rcJOINrole rONr.idrc.role_idWHEREr.parent_idISNOTNULL)SELECTDISTINCTp.resource,p.actionFROMrole_chain rcJOINrole_permission rpONrp.role_idrc.role_idJOINpermission pONp.idrp.permission_id;这段 SQL 在我这边 200ms 内能跑完用户 ~5000、角色 ~80、权限 ~600。生产建议加 Redis 缓存按user_id缓存权限集5 分钟 TTL角色变更时主动清。收尾RBAC 不是终点是起点回到开头那句话RBAC 权限模型解决的不是有没有权限而是权限变动成本。从 RBAC0 到 RBAC3、再到用户组和数据权限每加一层能力都是为了把某一类频繁又容易出错的手工操作沉淀进模型里。如果你只记一句话就记这句——让权限跟着角色走而不是跟着人走。如果你的业务还涉及按属性动态判断比如工作日 9-18 点才能导出那 RBAC 就到头了下一站是 ABAC基于属性的访问控制。这个我下篇再展开。