一级a做爰片 A视频网站,推荐几个好看的网站,专业开发手机网站建设,淘宝店铺怎么装修1. 核心架构设计
采用标准的 RBAC (Role-Based Access Control) 模型#xff0c;并在此基础上融合了 SaaS 多租户#xff08;Multi-Tenancy#xff09; 的隔离机制。
1.1 实体关系模型 (ERD)
系统的权限控制由以下五张核心表和一张租户约束表构成#xff1a;
system_user (…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 交互流程分析加载全量菜单树前端调用/list-all-simple。租户过滤关键后端并不直接返回system_menu的全量数据而是先查询当前租户对应的TenantPackage。逻辑Result AllMenus ∩ TenantPackageMenus。只有租户购买了的功能才会显示在可选列表中。加载已选菜单前端调用/list-role-menus?roleIdX。后端查询system_role_menu表返回该角色已拥有的菜单 ID 集合。提交变更前端提交roleId和新的menuIds集合。后端执行事务更新。2.2 核心代码实现增量更新 (Incremental Update)为了最小化数据库锁竞争和提高性能不采用“全删全插”策略而是通过计算差集进行精准操作。Transactional(rollbackForException.class)CacheEvict(valueRedisKeyConstants.MENU_ROLE_ID_LIST,allEntriestrue)// 清空关联缓存publicvoidassignRoleMenu(LongroleId,SetLongmenuIds){// 1. 查询当前数据库中该角色拥有的菜单 IDSetLongdbMenuIdsconvertSet(roleMenuMapper.selectListByRoleId(roleId),RoleMenuDO::getMenuId);// 2. 预处理前端传入的 ID 集合判空处理SetLongnewMenuIdsCollUtil.emptyIfNull(menuIds);// 3. 计算需要【新增】的集 (Create Set New - Old)CollectionLongcreateMenuIdsCollUtil.subtract(newMenuIds,dbMenuIds);// 4. 计算需要【删除】的集 (Delete Set Old - New)CollectionLongdeleteMenuIdsCollUtil.subtract(dbMenuIds,newMenuIds);// 5. 执行批量插入if(CollUtil.isNotEmpty(createMenuIds)){// 使用 Lambda 将 ID 列表转换为实体对象列表ListRoleMenuDObatchListCollectionUtils.convertList(createMenuIds,menuId-{RoleMenuDOentitynewRoleMenuDO();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 交互流程分析加载全量角色列表前端调用/role/list-all-simple。数据隔离后端强制追加WHERE tenant_id current_tenant_id防止越权查看其他租户的角色数据。加载已拥有角色前端调用/permission/list-user-roles?userIdY。后端查询system_user_role表返回该用户的角色 ID 集合。提交变更前端提交userId和新的roleIds集合。后端进行安全校验后执行更新。3.2 核心代码实现与安全校验Transactional(rollbackForException.class)CacheEvict(valueRedisKeyConstants.USER_ROLE_ID_LIST,key#userId)// 精确清除该用户的缓存publicvoidassignUserRole(LonguserId,SetLongroleIds){// 【安全校验】校验操作员是否有权分配这些角色// 防止低级别管理员分配高级别角色如普通管理员分配超级管理员权限validateRoleLevel(roleIds);// 1. 查询数据库中该用户当前的角色SetLongdbRoleIdsuserRoleMapper.selectRoleListByUserId(userId);// 2. 计算差集 (Diff)CollectionLongcreateRoleIdsCollUtil.subtract(roleIds,dbRoleIds);CollectionLongdeleteRoleIdsCollUtil.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 系统的鉴权逻辑是“正向遍历”加载根据UserId查Roles再根据Roles查出所有的Permissions集合。比对判断目标权限字符串如system:user:delete是否存在于该用户的权限集合中。缺点在“菜单与权限合一”的架构下若要获取用户的所有权限标识需要关联查询整张菜单表数据量大时效率略低。4.2 本系统的方案逆向白名单Reverse Lookup Strategy本系统采用的是“由权限找角色再看用户在不在角色里”的逻辑。核心思想不问“用户有什么权限”而是问“谁有资格访问这个权限”然后看用户是否在资格名单里。实现优势极度依赖 Redis 缓存将复杂的数据库关联转化为内存中的 Set 交集运算。核心代码逻辑推演 (hasAnyPermission方法)步骤一由【权限标识】反查【菜单 ID】系统首先询问“哪个菜单携带了system:user:delete这个标识”// 查缓存Permission - MenuId ListListLongmenuIdsmenuService.getMenuIdListByPermissionFromCache(permission);注这里返回 List 是为了兼容极端情况下多个不同菜单使用了同一个权限标识的情况。步骤二由【菜单 ID】反查【角色 ID 白名单】系统接着询问“ID 为1024的这个菜单被授权给了哪些角色”// 查缓存MenuId - RoleId Set (白名单)SetLongallowedRoleIdsroleMenuService.getMenuRoleIdListByMenuIdFromCache(menuId);结果得到一个允许访问的角色 ID 集合例如[1, 5]超级管理员、人事经理。步骤三获取【当前用户的角色集合】获取当前登录用户持有的角色列表。// 查缓存UserId - RoleId SetSetLonguserRoleIdsuserRoleService.getRoleListByUserIdFromCache(userId);结果例如[5, 6]人事经理、考勤员。步骤四集合交集运算 (Intersection)判断“用户角色集”与“权限白名单”是否有交集。// 只要有任意一个角色匹配即视为有权限if(CollUtil.containsAny(allowedRoleIds,userRoleIds)){returntrue;// 放行}returnfalse;// 拦截案例结果[1, 5]与[5, 6]存在交集5鉴权通过。