news 2026/9/1 16:13:40

SpringBoot开发企业后台-权限模型不用迷信RBAC可以去掉角色

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot开发企业后台-权限模型不用迷信RBAC可以去掉角色

SpringBoot开发企业后台-权限模型不用迷信RBAC可以去掉角色

工程判断:RBAC 用“用户—角色—权限”降低授权数量,是后台系统最常见的权限模型。但当组织较小、岗位变化频繁或角色只为某个人存在时,角色层也可能变成新的维护对象。

权限模型没有绝对标准,关键是授权关系是否可解释、变更是否可追踪、计算是否可缓存。为了套 RBAC 而制造大量临时角色,会让最终权限来源更难回答。

MetaLite 选择用户、组织与资源直接授权的轻量模型,减少角色中转,但保留资源与数据权限边界。本文先比较不同规模下的模型成本,再结合权限关系说明这一取舍适合什么系统。

一、经典 RBAC 解决的核心问题是什么

RBAC 的价值,不在于数据库里必须出现一张叫role的表,而在于避免直接为每个用户维护大量权限。

经典模型使用角色复用一组权限:

User ── UserRole ── Role ── RolePermission ── Permission

当岗位跨越组织边界,或者同一组织内存在大量职责组合时,角色是一层非常有价值的间接关系。

例如,同一个研发部门里可能同时存在:

  • 开发人员;
  • 测试人员;
  • 发布管理员;
  • 只读审计人员。

这些职责与组织并不完全等价。若强行只按部门授权,就会让所有成员获得相同权限。

所以问题不应该是“RBAC 好不好”,而应该是:

业务中的权限集合,究竟更稳定地附着在角色上,还是组织上?

二、MetaLite 为什么直接使用用户—组织—资源

MetaLite 管理后台的功能权限由三类核心数据组成:

关系当前实体含义
用户—组织SysUserOrgEntity一个用户可属于多个组织
组织—资源SysFuncPermissionEntity一个组织可分配多个资源
资源SysResEntity描述菜单、页面、按钮和后端接口

关系可以简化成:

User ── UserOrg ── Org ── FuncPermission ── Resource

设计重点是让组织同时承担两个职责:

  1. 表达人员归属;
  2. 承载一组功能权限。

当企业权限确实主要跟组织走时,它能减少一层重复映射。管理员不用先维护组织,再维护一套内容几乎相同的角色。

三、一个用户属于多个组织时,权限怎样计算

登录时,SysUserService.fillUserPermission先查询用户所属组织,只保留状态正常的关联:

List<String>orgIds=userOrgList.stream().filter(uo->StatusEnum.isNormal(uo.getStatus())).map(SysUserOrgEntity::getOrgId).toList();

然后一次查询这些组织关联的资源,并对资源 ID 去重:

List<String>resIds=funcPermList.stream().map(SysFuncPermissionEntity::getResId).distinct().toList();

因此,非管理员用户的最终功能权限是其所有正常组织权限的并集:

用户权限 = 组织 A 的资源 ∪ 组织 B 的资源 ∪ ……

这条规则易于解释,也容易排查。看到某个权限时,可以沿着“用户属于哪些组织—这些组织分配了哪些资源”反查来源。

但它只有增加,没有显式拒绝语义。当前模型中不存在“组织 A 允许、组织 B 禁止,最后禁止生效”的优先级规则。

四、一份资源同时驱动菜单、按钮和接口

SysResEntity当前把资源分为四类:

resType资源类型
0虚拟资源
1菜单
2页面
3按钮

资源中既有前端字段,也有后端字段:

privateStringcomponentPath;privateStringresPath;

其中componentPath可用于前端路由或按钮权限码,resPath表示需要控制的后端接口路径。

这让一次资源分配不只控制“菜单能不能看到”,还可以进入后端接口校验链路。

前端隐藏按钮只是体验控制,真正的安全边界仍必须在服务端。否则用户绕过页面直接发送请求,隐藏按钮没有任何保护作用。

五、后端如何判断当前接口是否需要权限

MetaLite 在API_RECEIVE入口切面中注册了ApiPermissionHandler

它的判断顺序是:

  1. 没有登录用户 ID 时放行,由网关承担登录校验;
  2. 获取当前请求 URI;
  3. 判断该 URI 是否属于需要授权的资源;
  4. 从缓存读取当前用户的登录与资源信息;
  5. 管理员直接放行;
  6. 非管理员按资源中的resPath做不区分大小写的精确匹配。

核心判断可以概括为:

booleanhasPermission=loginDto.getResList().stream().anyMatch(res->StringUtils.isNotBlank(res.getResPath())&&Strings.CI.equals(res.getResPath(),requestUri));

这里有两个值得注意的边界。

第一,当前是 URI 精确匹配,不是 Ant Path 通配、正则或“HTTP 方法 + URI”的组合授权。

第二,只有被资源配置识别为需要授权的接口才进入这一步。因此,资源配置本身也是安全配置的一部分,不能只依赖前端是否展示入口。

六、为什么把权限放进登录缓存

如果每次请求都连接数据库,依次查询用户组织、组织资源和资源详情,权限校验会给所有业务接口增加多次查询。

MetaLite 在登录时计算orgIdListresList,随后把登录信息写入 Redis。请求到来时,权限处理器读取缓存完成判断。

这是一种典型的读写取舍:

登录或授权变更时多做计算 ↓ 每次业务请求减少数据库访问

缓存不能只写不刷新。当前源码在以下动作后会刷新在线非管理员用户的权限:

  • 为组织重新分配资源;
  • 组织状态发生变化;
  • 用户加入或移出组织;
  • 删除组织及其关联关系;
  • 用户重新启用。

refreshUserPermissions会重新计算权限,然后更新 Redis 中的orgIdListresList

需要准确说明:这里只刷新当前仍有登录缓存的用户。离线用户无需更新,下一次登录时会重新计算。

当前源码还有一个值得继续收紧的边界:组织状态变化后虽然触发了刷新,但fillUserPermission过滤的是SysUserOrgEntity.status和资源状态,没有直接查询SysOrgEntity.status。如果停用组织时没有同步修改用户—组织关联状态,仅刷新缓存本身不能证明该组织的权限一定被排除。

更严谨的实现应在权限聚合查询中显式加入组织状态,或者在组织停用时以事务方式同步关联状态,并用集成测试验证在线用户权限立即收敛。文章不能把“调用了刷新方法”直接等同于“组织停用语义已经完整生效”。

七、省掉角色层,换来了什么,又失去了什么

组织直连资源的收益很直接:

  • 表和配置关系更少;
  • 权限来源更容易沿组织结构追踪;
  • 人员调入、调出组织即可改变权限;
  • 适合部门、门店、项目组主导授权的后台。

代价也同样明确:

  • 同一组织内不同岗位难以只靠当前模型区分;
  • 缺少跨组织复用的独立职责集合;
  • 多组织权限按并集合并,没有显式拒绝和优先级;
  • 权限模板、临时授权和职责分离需要额外设计。

如果业务开始频繁出现“同部门不同岗位”“跨部门同职责”,就说明独立角色层开始产生真实价值。那时可以演进为:

用户 → 组织 用户或组织 → 角色 → 资源

角色应该由业务复杂度引入,而不是因为权限教程里通常有五张表就预先加入。

八、功能权限不等于数据权限

本文讨论的是“能否访问某个菜单、按钮或接口”,属于功能权限。

“能够看到哪些部门、哪些订单、哪些客户”属于数据权限,需要真正落到查询条件、租户边界或数据过滤器中。

MetaLite 当前虽然存在数据权限配置相关实体和管理代码,但在本次源码核验中,没有找到它已经自动注入所有业务查询条件的完整执行链路。因此不能把功能权限的组织模型直接宣传为已完成数据权限隔离。

这两个问题必须分开验收:

功能权限:这个接口能不能调用? 数据权限:调用后能够读写哪些数据?

九、权限模型应该让排查路径更短

权限系统最难的往往不是第一次建表,而是半年后回答这些问题:

  • 这个用户为什么有这个按钮?
  • 调离部门后为什么还能调用接口?
  • 修改授权后为什么没有立即生效?
  • 前端没有菜单,后端为什么仍然放行?

MetaLite 选择用户—组织—资源直连,是因为在组织主导授权的系统里,这条路径更短、更符合管理员的实际操作。

它不是经典 RBAC 的万能替代品。真正可复用的设计原则是:先找到权限最稳定的业务载体,再决定是否需要角色这层间接关系。

十、用一个多组织用户还原权限并集

设用户 U 同时属于组织 A 和 B:A 拥有“查看用户”,B 拥有“重置密码”。登录聚合后,fillUserPermission应得到两个资源的去重并集。停用 U 与 B 的关系后重新计算,只应保留 A 的资源。

这组数据至少需要验证四个状态:关系正常、关系停用、组织停用、资源停用。当前源码对用户—组织关系状态有直接过滤,对组织状态的收敛还依赖管理链路触发刷新,因此不能只构造一次正常登录就宣布模型成立。

把权限来源输出为“资源来自哪个组织”的诊断信息,还能显著缩短线上排查时间;否则看到并集结果时,很难解释某个按钮究竟从哪条关系获得。


框架简介
MetaLite 是面向企业生产环境的新一代 Java 微服务技术底座。系列文章重点分享代码背后的设计思路、技术取舍与工程实践。

源码基线
JDK 21、Spring Boot 3.2.9、Spring Cloud 2023.0.1、Spring Cloud Alibaba 2023.0.1.3,具体组件版本以项目backend-bom为准。

作者简介
15 年 Spring 体系企业级开发经验,专注于 Java 微服务架构、工程治理与生产实践。

持续更新
MetaLite 系列内容将持续更新,围绕核心设计、源码链路、技术取舍与生产实践展开。欢迎关注作者,及时获取后续内容。

在线演示
演示地址: https://admin.metalite.top/
演示账号: guess
演示密码: admin@2026

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 16:11:41

丙烯酸聚氨酯面漆能直接刷混凝土吗?三层配套才是正解

混凝土看起来坚硬密实&#xff0c;实际上是一个布满微孔的。在电子显微镜下&#xff0c;混凝土表面布满相互连通的毛细通道&#xff0c;孔隙率可达10%-20%。当有人问"丙烯酸聚氨酯面漆能不能直接刷在混凝土上"时&#xff0c;答案只有一个字&#xff1a;不能。这不是面…

作者头像 李华
网站建设 2026/9/1 16:08:09

如何赋予 LLM 规划能力?

目录 引言&#xff1a;为什么大模型天然缺乏长链规划能力&#xff1f; 1.1 自回归生成的局部贪心陷阱 1.2 System 1&#xff08;快思考&#xff09;向 System 2&#xff08;慢思考&#xff09;的范式演进 1.3 规划&#xff08;Planning&#xff09;的核心形式化定义 LLM 规…

作者头像 李华
网站建设 2026/9/1 16:07:54

用傅里叶变换解码音色:频谱分析揭示声音的本质

从小学开始学音乐理论&#xff0c;总有几个词让人似懂非懂&#xff1a;“音色”“泛音”“明亮”“浑厚”。理工科的直觉会告诉你&#xff0c;这些词背后一定有物理量&#xff0c;但乐理书往往只告诉你“音色是声音的特色”&#xff0c;然后就没有然后了。 直到接触傅里叶变换…

作者头像 李华
网站建设 2026/9/1 16:07:41

STM32G431电机驱动板硬件设计:从FOC算法到稳定运行的电路解析

在实际嵌入式电机控制项目中&#xff0c;STM32G431系列MCU因其高性能的Cortex-M4内核、高精度定时器和丰富的模拟外设&#xff0c;成为实现FOC&#xff08;磁场定向控制&#xff09;等先进电机驱动算法的热门选择。然而&#xff0c;仅仅理解算法代码是不够的&#xff0c;一个稳…

作者头像 李华
网站建设 2026/9/1 15:58:33

java sdk 华为 HarmonyOS SDK 26 炸裂升级!8万接口狂飙,开发者不学就亏惨了

IT之家传来消息, 华为手机官方于今日宣告, SDK 26正式予以发布, 在易用性方面、空间化层面以及智能化层面均实现全面晋级。依据介绍, SDK通过将超过100个Kit予以开放、8万多API接口予以呈现, 其9大创新投向如下:据 IT 之家先前报道, 华为鸿蒙 7 在 6 月 12 日已正式予以发布, 官…

作者头像 李华
网站建设 2026/9/1 15:58:12

【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的水族箱温度水位增氧一体化控制系统 基于 STM32 或 51 单片机的嵌入式鱼缸环境监测与自动执行装置设计(025205)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华