news 2026/6/15 21:29:35

基于SpringBoot与RBAC的功能权限设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于SpringBoot与RBAC的功能权限设计

1. 核心架构设计

采用标准的RBAC (Role-Based Access Control)模型,并在此基础上融合了SaaS 多租户(Multi-Tenancy)的隔离机制。

1.1 实体关系模型 (ERD)

系统的权限控制由以下五张核心表和一张租户约束表构成:

  • system_user(用户表):主体,系统的登录账户。
  • system_role(角色表):权限的集合体,连接用户与菜单的桥梁。
  • system_menu(菜单/权限表):客体,定义了页面节点(Menu)和功能操作标识(Permission,如system:user:add)。
  • system_user_role(用户-角色关联表):实现多对多关系,决定用户拥有哪些角色。
  • system_role_menu(角色-菜单关联表):实现多对多关系,决定角色拥有哪些权限。
  • system_tenant_package(租户套餐表)前置约束。定义了该租户下所有角色能拥有的最大权限集合(全集 U 的子集)。

2. 模块一:角色菜单分配 (Role-Menu Authorization)

此模块的功能是定义“某个角色能做什么”。流程遵循“查询全量 -> 查询已选 -> 计算差集 -> 增量更新”的标准模式。

2.1 交互流程分析

  1. 加载全量菜单树
  • 前端调用/list-all-simple
  • 租户过滤(关键):后端并不直接返回system_menu的全量数据,而是先查询当前租户对应的TenantPackage
  • 逻辑Result = AllMenus ∩ TenantPackageMenus。只有租户购买了的功能,才会显示在可选列表中。
  1. 加载已选菜单
  • 前端调用/list-role-menus?roleId=X
  • 后端查询system_role_menu表,返回该角色已拥有的菜单 ID 集合。
  1. 提交变更
  • 前端提交roleId和新的menuIds集合。
  • 后端执行事务更新。

2.2 核心代码实现:增量更新 (Incremental Update)

为了最小化数据库锁竞争和提高性能,不采用“全删全插”策略,而是通过计算差集进行精准操作。

@Transactional(rollbackFor=Exception.class)@CacheEvict(value=RedisKeyConstants.MENU_ROLE_ID_LIST,allEntries=true)// 清空关联缓存publicvoidassignRoleMenu(LongroleId,Set<Long>menuIds){// 1. 查询当前数据库中该角色拥有的菜单 IDSet<Long>dbMenuIds=convertSet(roleMenuMapper.selectListByRoleId(roleId),RoleMenuDO::getMenuId);// 2. 预处理前端传入的 ID 集合(判空处理)Set<Long>newMenuIds=CollUtil.emptyIfNull(menuIds);// 3. 计算需要【新增】的集 (Create Set = New - Old)Collection<Long>createMenuIds=CollUtil.subtract(newMenuIds,dbMenuIds);// 4. 计算需要【删除】的集 (Delete Set = Old - New)Collection<Long>deleteMenuIds=CollUtil.subtract(dbMenuIds,newMenuIds);// 5. 执行批量插入if(CollUtil.isNotEmpty(createMenuIds)){// 使用 Lambda 将 ID 列表转换为实体对象列表List<RoleMenuDO>batchList=CollectionUtils.convertList(createMenuIds,menuId->{RoleMenuDOentity=newRoleMenuDO();entity.setRoleId(roleId);entity.setMenuId(menuId);returnentity;});roleMenuMapper.insertBatch(batchList);}// 6. 执行批量删除if(CollUtil.isNotEmpty(deleteMenuIds)){roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId,deleteMenuIds);}}

2.3 技术要点

  • 缓存清理:使用@CacheEvict(allEntries = true)。由于角色菜单的变更可能影响大量用户的权限判定,且难以精确计算受影响的 Key,因此选择直接清空该缓存命名空间下的所有数据,利用 Redis 的 SCAN/DEL 机制保证数据强一致性。

3. 模块二:用户角色分配 (User-Role Assignment)

此模块的功能是定义“某个用户是谁”。其实现逻辑与角色菜单高度对称。

3.1 交互流程分析

  1. 加载全量角色列表
  • 前端调用/role/list-all-simple
  • 数据隔离:后端强制追加WHERE tenant_id = current_tenant_id,防止越权查看其他租户的角色数据。
  1. 加载已拥有角色
  • 前端调用/permission/list-user-roles?userId=Y
  • 后端查询system_user_role表,返回该用户的角色 ID 集合。
  1. 提交变更
  • 前端提交userId和新的roleIds集合。
  • 后端进行安全校验后执行更新。

3.2 核心代码实现与安全校验

@Transactional(rollbackFor=Exception.class)@CacheEvict(value=RedisKeyConstants.USER_ROLE_ID_LIST,key="#userId")// 精确清除该用户的缓存publicvoidassignUserRole(LonguserId,Set<Long>roleIds){// 【安全校验】:校验操作员是否有权分配这些角色// 防止低级别管理员分配高级别角色(如普通管理员分配超级管理员权限)validateRoleLevel(roleIds);// 1. 查询数据库中该用户当前的角色Set<Long>dbRoleIds=userRoleMapper.selectRoleListByUserId(userId);// 2. 计算差集 (Diff)Collection<Long>createRoleIds=CollUtil.subtract(roleIds,dbRoleIds);Collection<Long>deleteRoleIds=CollUtil.subtract(dbRoleIds,roleIds);// 3. 执行数据库操作 (逻辑同上)if(CollUtil.isNotEmpty(createRoleIds)){userRoleMapper.insertBatch(userId,createRoleIds);}if(CollUtil.isNotEmpty(deleteRoleIds)){userRoleMapper.deleteListByUserIdAndRoleIds(userId,deleteRoleIds);}}

3.3 技术要点

  • 精确缓存清理:与 Role-Menu 不同,这里只影响单个用户的权限。因此@CacheEvict使用key = "#userId",通过 SpEL 表达式精确删除user_role_ids::userId这一条 Redis 记录,避免误伤其他用户缓存。
  • 权限级别管控:在分配前必须校验Role Level,确保Level(操作员) >= Level(目标角色)

4. 运行时鉴权逻辑 (Runtime Verification)

在判断“用户是否有权执行某个操作”时,本系统摒弃了教科书式的“正向查找”,采用了基于缓存的“逆向倒推”策略。这种设计源于“菜单即权限”的数据结构(即权限标识寄生在菜单表中,没有独立的权限表)。

4.1 常规方案(Standard RBAC)

大多数标准 RBAC 系统的鉴权逻辑是“正向遍历”

  1. 加载:根据UserIdRoles,再根据Roles查出所有的Permissions集合。
  2. 比对:判断目标权限字符串(如system:user:delete)是否存在于该用户的权限集合中。
  • 缺点:在“菜单与权限合一”的架构下,若要获取用户的所有权限标识,需要关联查询整张菜单表,数据量大时效率略低。

4.2 本系统的方案:逆向白名单(Reverse Lookup Strategy)

本系统采用的是“由权限找角色,再看用户在不在角色里”的逻辑。

  • 核心思想:不问“用户有什么权限”,而是问“谁有资格访问这个权限”,然后看用户是否在资格名单里。
  • 实现优势:极度依赖 Redis 缓存,将复杂的数据库关联转化为内存中的 Set 交集运算。
核心代码逻辑推演 (hasAnyPermission方法)

步骤一:由【权限标识】反查【菜单 ID】
系统首先询问:“哪个菜单携带了system:user:delete这个标识?”

// 查缓存:Permission -> MenuId ListList<Long>menuIds=menuService.getMenuIdListByPermissionFromCache(permission);
  • 注:这里返回 List 是为了兼容极端情况下,多个不同菜单使用了同一个权限标识的情况。

步骤二:由【菜单 ID】反查【角色 ID 白名单】
系统接着询问:“ID 为1024的这个菜单,被授权给了哪些角色?”

// 查缓存:MenuId -> RoleId Set (白名单)Set<Long>allowedRoleIds=roleMenuService.getMenuRoleIdListByMenuIdFromCache(menuId);
  • 结果:得到一个允许访问的角色 ID 集合,例如[1, 5](超级管理员、人事经理)。

步骤三:获取【当前用户的角色集合】
获取当前登录用户持有的角色列表。

// 查缓存:UserId -> RoleId SetSet<Long>userRoleIds=userRoleService.getRoleListByUserIdFromCache(userId);
  • 结果:例如[5, 6](人事经理、考勤员)。

步骤四:集合交集运算 (Intersection)
判断“用户角色集”“权限白名单”是否有交集。

// 只要有任意一个角色匹配,即视为有权限if(CollUtil.containsAny(allowedRoleIds,userRoleIds)){returntrue;// 放行}returnfalse;// 拦截
  • 案例结果:[1, 5][5, 6]存在交集5,鉴权通过。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 14:36:04

NCM解密工具:打破音乐格式壁垒,让加密音频重获新生

你是否曾经下载了心爱的网易云音乐&#xff0c;却因为NCM加密格式而无法在其他设备上播放&#xff1f;这种平台限制让音乐体验大打折扣。别担心&#xff0c;NCM解密工具就是你的技术伙伴&#xff0c;它能轻松解除NCM文件的加密束缚&#xff0c;让音乐真正属于你。 【免费下载链…

作者头像 李华
网站建设 2026/6/15 12:54:15

PyTorch与C++集成:通过TorchScript部署生产环境

PyTorch与C集成&#xff1a;通过TorchScript部署生产环境 在构建高并发、低延迟的AI服务时&#xff0c;一个常见的困境浮出水面&#xff1a;研究阶段用PyTorch写模型非常灵活高效&#xff0c;但一旦进入线上部署&#xff0c;Python的运行时开销和GIL限制就成了性能瓶颈。更不用…

作者头像 李华
网站建设 2026/6/15 11:45:40

PyTorch模型部署Kubernetes集群管理GPU资源

PyTorch模型部署Kubernetes集群管理GPU资源 在当今AI驱动的业务场景中&#xff0c;企业不再满足于“模型能跑”&#xff0c;而是追求“高效、稳定、可扩展”的生产级部署。一个训练好的PyTorch模型&#xff0c;若无法快速上线、弹性伸缩并充分利用昂贵的GPU资源&#xff0c;其…

作者头像 李华
网站建设 2026/6/15 11:46:52

显卡优化神器NVIDIA Profile Inspector:解锁隐藏性能的终极指南

显卡优化神器NVIDIA Profile Inspector&#xff1a;解锁隐藏性能的终极指南 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 还在为游戏卡顿、画面撕裂而烦恼吗&#xff1f;想要充分释放显卡潜能却不知从…

作者头像 李华
网站建设 2026/6/15 11:50:55

3分钟掌握UML绘图:零安装在线编辑器的终极指南

3分钟掌握UML绘图&#xff1a;零安装在线编辑器的终极指南 【免费下载链接】plantuml-editor PlantUML online demo client 项目地址: https://gitcode.com/gh_mirrors/pl/plantuml-editor 还在为复杂的UML绘图软件头疼吗&#xff1f;这款免费的在线UML绘图工具让你彻底…

作者头像 李华
网站建设 2026/6/14 16:52:20

PyTorch模型预测接口封装为gRPC服务(GPU后端)

PyTorch模型预测接口封装为gRPC服务&#xff08;GPU后端&#xff09; 在当前AI系统日益走向生产化的背景下&#xff0c;如何将训练好的深度学习模型高效、稳定地部署为可调用的服务&#xff0c;已成为工程落地的关键一环。尤其是在图像识别、语音处理等实时性要求高的场景中&am…

作者头像 李华