当前位置: 首页 > news >正文

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

http://www.cnnetsun.cn/news/4355848.html

相关文章:

  • 丙烯酸聚氨酯面漆能直接刷混凝土吗?三层配套才是正解
  • 如何赋予 LLM 规划能力?
  • 用傅里叶变换解码音色:频谱分析揭示声音的本质
  • STM32G431电机驱动板硬件设计:从FOC算法到稳定运行的电路解析
  • java sdk 华为 HarmonyOS SDK 26 炸裂升级!8万接口狂飙,开发者不学就亏惨了
  • 【单片机课程设计/毕业设计】基于 STM32 或 51 单片机的水族箱温度水位增氧一体化控制系统 基于 STM32 或 51 单片机的嵌入式鱼缸环境监测与自动执行装置设计(025205)
  • Zynq7000--AXI CDMA + BRAM 从PS侧DDR搬运数据记录
  • 基于SpringBoot的酒水销售系统的设计与实现(源代码+文档+PPT+调试+讲解)
  • 计算机单片机毕设实战-基于 STM32 或 51 单片机的 WiFi 智能鱼缸软硬件协同设计 基于 STM32 或 51 单片机的水族箱多参数自动管控系统设计(025205)
  • 三类技术背景转型AI安全:CAIDCP认证与对抗样本实战指南
  • SWAT模型高阶应用:从无资料流域建模到情景模拟的完整工程实践
  • PECR:一种可复现的、基于遥测信息的SD-WAN漏洞优先级排序规范与综合压力测试用于SD-WAN漏洞优先级排序的PECR
  • 兽剧预告怎么拆解?以《愚行录》第二支预告为例
  • 网易人机交互算法工程师笔试题全解析:考点、易错点与复习路线
  • 选择GEO接口对接定制开发服务要参考哪些核心适配标准?
  • 【单片机课设毕设项目】基于单片机的 LCD1602 显示智能加湿设备设计开发 基于 STM32 或 51 单片机的继电器驱动智能加湿调控系统设计(024905)
  • 2026年实测这3个高性价比降AIGC网站,毕业论文AI率检测一次过不返工!
  • 华为OD机试新系统C卷备考指南:题型拆解与实战技巧
  • Unity游戏开发入门:从零构建3D平台跳跃游戏Demo
  • CST 电磁仿真 GPU 加速性能实测报告-2026 最新版
  • 计算机毕业设计之基于Java Web的药店管理系统设计与实现
  • 8.STM32 串口通信程序编写详解:寄存器与 HAL 库实战
  • AI编程工作流实战:从零构建Flask API项目
  • 【2014-08-18】Django自学笔记:模板
  • 【three.js教程】Three.js 加载 3D 模型(Loading 3D Models):选对格式,少踩一半坑
  • 基于SpringBoot的毕业设计导师分配系统(源码+lw+部署文档+讲解等)
  • 从“盯屏幕发呆”到“一键生成初稿”:毕夏AI官网正在重新定义毕业论文写作这件事
  • Nacos服务实例频繁掉线:系统性排查框架与解决方案
  • 爱普生L系列打印机查询IP地址与联网状态排查指南
  • Vue2/Vue3中使用hiprint实现可视化打印设计与报表打印的实践