# 角色权限管理 **本文引用的文件** - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [application.yml](file://crm-app/src/main/resources/application.yml) - [AuthProperties.java](file://crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java) - [DataInitializer.java](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java) - [PermissionConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java) - [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java) - [AuthConstants.java](file://crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [LoginResultDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java) - [UserInfoDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/UserInfoDTO.java) - [RoleDetailVO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDetailVO.java) - [AuthIdentity.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthIdentity.java) - [AuthUser.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthUser.java) - [SysDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysDept.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [SysUserDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserDept.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [IdentityTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/IdentityTypeEnum.java) - [MenuTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuTypeEnum.java) - [LoginParam.java](file://crm-auth/src/main/java/com/crm/auth/domain/param/LoginParam.java) - [AuthIdentityMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/AuthIdentityMapper.java) - [AuthUserMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/AuthUserMapper.java) - [SysDeptMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysDeptMapper.java) - [SysMenuMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysMenuMapper.java) - [SysRoleMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysRoleMapper.java) - [SysRoleMenuMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysRoleMenuMapper.java) - [SysUserDeptMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysUserDeptMapper.java) - [SysUserRoleMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysUserRoleMapper.java) - [AuthLoginUser.java](file://crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [SecurityExceptionHandlers.java](file://crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java) - [ThirdPartyAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClient.java) - [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java) - [ThirdPartyUserInfo.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyUserInfo.java) - [AuthIdentityServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthIdentityServiceImpl.java) - [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [AuthUserServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthUserServiceImpl.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [SysDeptServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysDeptServiceImpl.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [ISysMenuService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysMenuService.java) - [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java) - [IAuthIdentityService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthIdentityService.java) - [IAuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [IAuthUserService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthUserService.java) - [ISysDept.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysDept.java) - [ISysMenuService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysMenuService.java) - [ISysRoleService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java) - [HasValueEnum.java](file://crm-base/src/main/java/com/crm/base/domain/enums/HasValueEnum.java) - [StatusEnum.java](file://crm-base/src/main/java/com/crm/base/domain/enums/StatusEnum.java) - [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java) - [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java) - [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java) - [SnowflakeProperties.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeProperties.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileInfo.java](file://crm-file/src/main/java/com/crm/file/domain/entity/FileInfo.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) ## 更新摘要 **变更内容** - 新增 RoleController RESTful 接口,提供完整的角色管理功能(分页、保存、详情、资源分配、删除) - 实现 SysRoleServiceImpl 业务逻辑增强,包含内置角色保护、级联删除、数据范围校验 - 新增 ISysMenuService.getAncestorIds() 祖先补全功能,支持菜单层级自动补全 - 引入 RoleDetailVO 响应结构,包含已补全祖先的完整授权集合 - 增强 DataInitializer 数据初始化器,创建内置管理员角色和角色管理权限点 - 完善权限种子数据,确保系统启动时具备基础权限配置 - 新增16个单元测试用例,覆盖角色验证场景、内置角色保护和级联删除行为 ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能与缓存优化](#性能与缓存优化) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录:最佳实践与常见场景](#附录最佳实践与常见场景) ## 简介 本技术文档围绕角色权限管理模块,系统性阐述基于RBAC(基于角色的访问控制)的权限模型设计与实现。内容涵盖角色定义、权限分配、用户-角色关联、数据范围控制、权限继承与合并、动态权限控制、权限缓存机制与性能优化策略,并提供常见业务场景的实现方案与最佳实践。读者可据此快速理解系统权限体系并指导二次开发。 **更新** 本次更新重点介绍了全新的 RoleController RESTful 接口、祖先补全系统、内置角色保护机制以及增强的权限种子数据初始化功能。新增了完整的测试覆盖,确保角色管理的健壮性和可靠性。 ## 项目结构 权限相关能力集中在 crm-auth 模块,基础安全与通用能力在 crm-base 模块,其他模块通过接口或配置接入。整体采用分层架构: - 表现层:控制器暴露登录、鉴权、系统管理等API - 服务层:封装认证、授权、角色菜单、部门等核心业务逻辑 - 数据层:MyBatis-Plus Mapper 映射实体到数据库 - 安全层:JWT过滤器、拦截器、异常处理、上下文工具 - 基础层:通用实体、枚举、异常、结果封装、数据可见性上下文、Redis配置、雪花ID等 ```mermaid graph TB subgraph "应用入口" App["CrmAppApplication"] Yml["application.yml"] end subgraph "认证与授权(crm-auth)" Cfg["SecurityConfig / PermissionConfig"] Ctrl["AuthController / SystemController / RoleController"] Svc["AuthService / PermissionService / RoleService / MenuService"] Sec["JwtAuthenticationFilter / DataScopeInterceptor / TokenService"] Ent["SysRole / SysMenu / SysUserRole / SysRoleMenu / AuthUser / ..."] Map["*Mapper 接口"] end subgraph "基础能力(crm-base)" BaseEnt["BaseEntity / OwnedEntity"] SecBase["LoginUser / SecurityUtils / DataScopeHelper / DataVisibilityContext"] Redis["RedisConfig"] Id["SnowflakeIdWorker"] Ex["全局异常处理器 / 权限异常"] end subgraph "文件服务(crm-file)" FileCtrl["FileController"] FileSvc["IFileInfoService / FileInfoServiceImpl"] end App --> Cfg Cfg --> Sec Ctrl --> Svc Svc --> Map Map --> Ent Sec --> SecBase Svc --> Redis FileCtrl --> FileSvc ``` **图表来源** - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [application.yml](file://crm-app/src/main/resources/application.yml) - [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java) - [PermissionConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java) - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [AuthUser.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthUser.java) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) 章节来源 - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [application.yml](file://crm-app/src/main/resources/application.yml) ## 核心组件 - 角色与菜单模型:角色、菜单、用户-角色、角色-菜单关联构成RBAC核心骨架 - 认证流程:JWT过滤器解析令牌,构建登录用户上下文 - 授权校验:方法级注解+拦截器/切面进行权限判定 - 数据范围控制:基于注解的数据范围拦截器,结合上下文注入SQL条件 - 权限缓存:角色-菜单、用户-角色、部门树等热点数据缓存于Redis - 统一异常与结果:标准化错误码与响应体 - **新增** 角色管理RESTful接口:提供完整的CRUD操作和资源分配功能 - **新增** 祖先补全系统:自动补全菜单层级关系的父节点 - **新增** 内置角色保护:防止误删或修改系统关键角色 - **新增** 完善的测试覆盖:16个单元测试用例确保业务逻辑正确性 章节来源 - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) ## 架构总览 下图展示从请求进入、认证、授权到数据范围控制的完整链路,以及缓存参与点。 ```mermaid sequenceDiagram participant Client as "客户端" participant API as "AuthController/SystemController/RoleController" participant Filter as "JwtAuthenticationFilter" participant Svc as "AuthService/PermissionService/RoleService" participant Cache as "Redis(权限/角色/菜单/部门树)" participant DB as "数据库" participant Interceptor as "DataScopeInterceptor" Client->>API : "HTTP请求" API->>Filter : "进入安全过滤器链" Filter->>Filter : "解析JWT/校验签名" Filter->>Svc : "加载用户角色与权限" Svc->>Cache : "读取缓存(命中则返回)" alt 缓存未命中 Svc->>DB : "查询用户-角色、角色-菜单" DB-->>Svc : "原始数据" Svc->>Cache : "写入缓存" end Filter-->>API : "设置Security上下文" API->>Svc : "调用业务方法(含权限注解)" Svc->>Interceptor : "数据范围拦截" Interceptor->>Cache : "读取数据范围配置" Interceptor->>DB : "拼接WHERE条件执行" Interceptor-->>Svc : "返回受限数据集" Svc-->>API : "业务结果" API-->>Client : "JSON响应" ``` **图表来源** - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) ## 详细组件分析 ### RBAC数据模型与关系 - 角色(SysRole):定义一组权限集合,支持内置角色标记和数据范围控制 - 菜单(SysMenu):代表系统功能节点(按钮/页面/接口),支持层级结构 - 用户-角色(SysUserRole):多对多关联 - 角色-菜单(SysRoleMenu):多对多关联,存储已补全祖先的完整授权集合 - 用户(AuthUser)、部门(SysDept)、用户-部门(SysUserDept):用于数据范围控制 ```mermaid erDiagram SYS_ROLE { bigint id PK varchar name varchar code int status datetime created_at datetime updated_at boolean builtin } SYS_MENU { bigint id PK varchar name varchar path int type int sort bigint parent_id FK datetime created_at datetime updated_at } AUTH_USER { bigint id PK varchar username varchar password int status datetime created_at datetime updated_at } SYS_DEPT { bigint id PK varchar name bigint parent_id FK int status datetime created_at datetime updated_at } SYS_USER_ROLE { bigint user_id FK bigint role_id FK } SYS_ROLE_MENU { bigint role_id FK bigint menu_id FK } SYS_USER_DEPT { bigint user_id FK bigint dept_id FK } AUTH_USER ||--o{ SYS_USER_ROLE : "拥有" SYS_ROLE ||--o{ SYS_USER_ROLE : "被赋予" SYS_ROLE ||--o{ SYS_ROLE_MENU : "包含" SYS_MENU ||--o{ SYS_ROLE_MENU : "被包含" AUTH_USER ||--o{ SYS_USER_DEPT : "属于" SYS_DEPT ||--o{ SYS_USER_DEPT : "包含" ``` **图表来源** - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [AuthUser.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthUser.java) - [SysDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysDept.java) - [SysUserDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserDept.java) 章节来源 - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [AuthUser.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthUser.java) - [SysDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysDept.java) - [SysUserDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserDept.java) ### 认证与授权流程 - 登录:控制器接收参数,服务层校验身份,生成JWT并返回 - 鉴权:过滤器解析JWT,构造登录用户上下文;服务层加载用户角色与权限,优先读缓存,未命中回源DB并回填缓存 - 授权:方法级注解驱动,拦截器根据当前用户权限集合判断是否允许访问 ```mermaid sequenceDiagram participant Client as "客户端" participant AuthCtrl as "AuthController" participant RoleCtrl as "RoleController" participant AuthService as "AuthService" participant PermSvc as "PermissionService" participant JWT as "JwtAuthenticationFilter" participant Cache as "Redis" participant DB as "数据库" Client->>AuthCtrl : "POST /auth/login" AuthCtrl->>AuthService : "login(LoginParam)" AuthService->>DB : "校验用户名/密码" AuthService-->>AuthCtrl : "用户信息" AuthService->>PermSvc : "获取用户角色与权限" PermSvc->>Cache : "读取缓存" alt 未命中 PermSvc->>DB : "查询用户-角色、角色-菜单" DB-->>PermSvc : "数据" PermSvc->>Cache : "写入缓存" end AuthService-->>AuthCtrl : "生成JWT" AuthCtrl-->>Client : "{token, userInfo}" Client->>RoleCtrl : "POST /api/roles/page" RoleCtrl->>PermSvc : "检查权限 'crm : role : list'" PermSvc->>Cache : "验证权限" Cache-->>RoleCtrl : "权限验证结果" RoleCtrl-->>Client : "角色列表数据" Client->>JWT : "携带Token访问受保护接口" JWT->>JWT : "解析并校验Token" JWT->>PermSvc : "加载权限(带缓存)" JWT-->>Client : "放行或拒绝" ``` **图表来源** - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) 章节来源 - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) ### 数据范围控制(行级权限) - 注解驱动:使用自定义注解声明数据范围级别 - 上下文:将当前用户、组织、角色等信息放入可见性上下文 - 拦截器:在查询前注入WHERE条件,限制数据可见范围 - 枚举:定义数据范围类型(如本人、本部门、本部门及子部门、全部等) ```mermaid flowchart TD Start(["进入查询方法"]) --> CheckAnno["检查@DataScope注解"] CheckAnno --> HasAnno{"存在注解?"} HasAnno --> |否| Proceed["直接执行查询"] HasAnno --> |是| BuildCtx["构建数据可见性上下文"] BuildCtx --> ReadScope["读取数据范围级别"] ReadScope --> InjectSql["拼接WHERE条件"] InjectSql --> ExecQuery["执行SQL"] ExecQuery --> End(["返回受限结果集"]) Proceed --> End ``` **图表来源** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) 章节来源 - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) ### 权限缓存与刷新 - 缓存对象:用户权限集合、角色-菜单映射、部门树 - 缓存键设计:以用户ID、角色ID、部门ID等为维度 - 更新策略:角色/菜单/用户-角色变更时,主动失效相关缓存 - 降级策略:缓存不可用时回源DB,保证可用性 ```mermaid classDiagram class 权限缓存管理器 { +getPermissions(userId) Set~String~ +getRoleMenus(roleId) Set~Long~ +getDeptTree(deptId) Dept[] +invalidateByUser(userId) +invalidateByRole(roleId) +invalidateByDept(deptId) } class Redis配置 { +连接池 +序列化策略 +过期策略 } class 权限服务 { +loadUserPermissions(userId) +syncToCache() } 权限缓存管理器 --> Redis配置 : "使用" 权限服务 --> 权限缓存管理器 : "读写" ``` **图表来源** - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java) 章节来源 - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) ### 动态权限控制 - 方法级注解:在控制器或服务方法上声明所需权限标识 - 运行时判定:拦截器/切面读取当前用户权限集合进行匹配 - 细粒度控制:支持资源级(菜单/按钮)与操作级(增删改查)组合 - 扩展点:新增权限标识无需重启,仅需配置与缓存刷新 章节来源 - [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java) - [PermissionConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) ### 角色管理RESTful接口 **新增** 完整的角色管理RESTful接口,遵循非严格RESTful规范,使用POST + 动作后缀的方式处理写操作。 #### 接口概览 - `POST /api/roles/page` - 分页查询角色(权限:`crm:role:list`) - `POST /api/roles/saveOrUpdate` - 新增或编辑角色(权限:`crm:role:save`) - `GET /api/roles/detail` - 角色详情,包含授权资源集合(权限:`crm:role:detail`) - `POST /api/roles/assign-resources` - 分配角色权限资源,支持祖先补全(权限:`crm:role:assign`) - `POST /api/roles/delete` - 删除角色,级联清理关联表(权限:`crm:role:delete`) #### 角色详情响应结构 **新增** RoleDetailVO响应结构,包含角色基本信息和已补全祖先的完整授权集合: - 基本字段:id、roleName、roleCode、dataScope、sort、remark - 特殊字段:builtin(是否内置角色)、resourceIds(已补全祖先的完整授权集合) ```mermaid sequenceDiagram participant Client as "客户端" participant RoleCtrl as "RoleController" participant RoleSvc as "SysRoleServiceImpl" participant MenuSvc as "SysMenuServiceImpl" participant DB as "数据库" Client->>RoleCtrl : "POST /api/roles/assign-resources" RoleCtrl->>RoleSvc : "assignResources(roleId, resourceIds)" RoleSvc->>MenuSvc : "getAncestorIds(resourceIds)" MenuSvc->>DB : "批量查询所有菜单" DB-->>MenuSvc : "菜单列表" MenuSvc->>MenuSvc : "内存遍历补全祖先" MenuSvc-->>RoleSvc : "完整授权集合" RoleSvc->>DB : "先删除后插入关联关系" DB-->>RoleSvc : "操作结果" RoleSvc-->>RoleCtrl : "处理完成" RoleCtrl-->>Client : "成功响应" ``` **图表来源** - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) 章节来源 - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [RoleDetailVO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDetailVO.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) ### 祖先补全系统 **新增** 智能的菜单层级祖先补全功能,前端只需传递勾选的叶子节点,后端自动补全所有父节点。 #### 核心特性 - 批量查询优化:一次性查询全表构建ID→parentId映射 - 内存遍历算法:沿parentId向上遍历收集祖先节点 - 去重处理:使用LinkedHashSet确保结果唯一性和顺序 - 空值保护:处理null、0L等边界情况 ```mermaid flowchart TD Input["输入菜单ID集合"] --> CheckEmpty{"是否为空?"} CheckEmpty --> |是| ReturnEmpty["返回空集合"] CheckEmpty --> |否| BatchQuery["批量查询所有菜单"] BatchQuery --> BuildMap["构建ID→parentId映射"] BuildMap --> IterateNodes["遍历输入节点"] IterateNodes --> TraverseUp["沿parentId向上遍历"] TraverseUp --> CollectAncestors["收集祖先节点"] CollectAncestors --> Deduplicate["去重处理"] Deduplicate --> ReturnResult["返回完整授权集合"] ReturnEmpty --> End(["结束"]) ReturnResult --> End ``` **图表来源** - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [ISysMenuService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysMenuService.java) 章节来源 - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [ISysMenuService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysMenuService.java) ### 内置角色保护机制 **新增** 完善的内置角色保护机制,防止误删或修改系统关键角色。 #### 保护规则 - 创建保护:禁止通过接口创建内置角色(builtin=true) - 编辑保护:内置角色的编码不可修改 - 删除保护:内置角色不允许删除 - 错误码:统一的CODE_BUILTIN_ROLE_PROTECTED错误码 #### 数据初始化增强 **更新** DataInitializer增强了权限种子数据初始化: - 自动创建内置管理员角色(ROLE_ADMIN) - 创建系统管理菜单结构 - 初始化角色管理相关的权限点 - 为管理员角色绑定所有菜单和权限 - 使用MenuSeed记录类型消除参数歧义 ```mermaid flowchart TD Start(["角色操作"]) --> CheckType{"是否内置角色?"} CheckType --> |否| NormalFlow["正常业务流程"] CheckType --> |是| ValidateOp{"操作类型"} ValidateOp --> |创建| RejectCreate["拒绝创建"] ValidateOp --> |编辑| ValidateEdit["验证编辑操作"] ValidateOp --> |删除| RejectDelete["拒绝删除"] ValidateEdit --> CanEdit{"是否修改编码?"} CanEdit --> |是| RejectEdit["拒绝修改编码"] CanEdit --> |否| AllowEdit["允许编辑"] RejectCreate --> Error["抛出保护异常"] RejectEdit --> Error RejectDelete --> Error AllowEdit --> NormalFlow NormalFlow --> Success["操作成功"] Error --> End(["结束"]) Success --> End ``` **图表来源** - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [DataInitializer.java](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java) - [AuthConstants.java](file://crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java) 章节来源 - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [DataInitializer.java](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java) - [AuthConstants.java](file://crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java) ### 测试覆盖与质量保证 **新增** 完整的单元测试覆盖,确保角色管理功能的健壮性。 #### 测试场景 - 角色编码唯一性验证 - 内置角色保护机制 - 数据范围合法性校验 - 基本字段非空验证 - 权限分配的祖先补全逻辑 - 角色详情查询功能 - 级联删除行为验证 #### 测试覆盖率 - 16个单元测试用例 - 覆盖所有核心业务逻辑 - 使用Mockito进行依赖模拟 - 断言库验证预期行为 ```mermaid flowchart TD TestSuite["测试套件"] --> UnitTests["单元测试"] UnitTests --> RoleValidation["角色验证测试"] UnitTests --> BuiltinProtection["内置角色保护测试"] UnitTests --> CascadeDeletion["级联删除测试"] UnitTests --> AncestorCompletion["祖先补全测试"] RoleValidation --> CodeUniqueness["编码唯一性"] RoleValidation --> DataScopeValidation["数据范围验证"] BuiltinProtection --> CreateProtection["创建保护"] BuiltinProtection --> EditProtection["编辑保护"] BuiltinProtection --> DeleteProtection["删除保护"] CascadeDeletion --> MenuCleanup["菜单关联清理"] CascadeDeletion --> UserRoleCleanup["用户角色清理"] ``` **图表来源** - [SysRoleServiceImplTest.java](file://crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java) 章节来源 - [SysRoleServiceImplTest.java](file://crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java) ## 依赖关系分析 - 控制器依赖服务层,服务层依赖Mapper与缓存 - 安全过滤器依赖服务层加载权限,依赖Redis缓存 - 数据范围拦截器依赖上下文与枚举,影响所有查询路径 - 基础模块提供通用实体、异常、结果、工具类与安全上下文 - **新增** RoleController依赖ISysRoleService和权限验证 - **新增** SysRoleServiceImpl依赖ISysMenuService进行祖先补全 ```mermaid graph LR Ctrl["控制器层"] --> Svc["服务层"] Svc --> Mapper["Mapper层"] Svc --> Cache["Redis缓存"] Filter["安全过滤器"] --> Svc Interceptor["数据范围拦截器"] --> Context["可见性上下文"] Svc --> Base["基础模块(BaseEntity/异常/结果)"] RoleCtrl["RoleController"] --> RoleSvc["SysRoleServiceImpl"] RoleSvc --> MenuSvc["SysMenuServiceImpl"] MenuSvc --> MenuMapper["SysMenuMapper"] ``` **图表来源** - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) 章节来源 - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [RoleController.java](file://crm-auth/src/main/java/com/crm/auth/controller/RoleController.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) ## 性能与缓存优化 - 热点数据缓存:用户权限、角色-菜单、部门树均落Redis,降低DB压力 - 批量查询:角色-菜单、用户-角色采用批量聚合,减少往返次数 - 懒加载与按需加载:仅在首次访问或缓存失效时加载 - 合理过期:为不同缓存设置差异化TTL,避免雪崩 - 防穿透:空值缓存与布隆过滤器(可选) - 降级与熔断:缓存不可用自动回源,保障可用性 - **新增** 祖先补全优化:批量查询全表构建映射,避免逐节点查询 - **新增** 全量替换策略:先删除后插入,确保数据一致性 章节来源 - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) ## 故障排查指南 - 登录失败:检查用户名/密码、第三方登录配置、Token生成逻辑 - 鉴权失败:确认用户是否具备对应角色与权限,检查缓存一致性 - 数据范围异常:核对@DataScope注解与上下文是否正确注入 - 缓存问题:检查Redis连通性、序列化、过期策略与失效时机 - 统一异常:查看全局异常处理器输出,定位具体错误码与堆栈 - **新增** 角色操作异常:检查内置角色保护、权限分配完整性、祖先补全逻辑 - **新增** 权限点缺失:验证DataInitializer是否正常执行,检查权限点绑定关系 - **新增** 测试失败:检查Mock配置、断言逻辑、依赖注入 章节来源 - [SecurityExceptionHandlers.java](file://crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [DataInitializer.java](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) ## 结论 本权限模块以RBAC为核心,结合JWT认证、方法级注解授权、数据范围控制与Redis缓存,形成高内聚、低耦合、可扩展的权限体系。通过合理的缓存策略与异常处理,兼顾了性能与稳定性。 **更新** 本次增强引入了完整的角色管理RESTful接口、智能的祖先补全系统、完善的内置角色保护机制以及增强的权限种子数据初始化功能,显著提升了系统的易用性、安全性和可维护性。新增的16个单元测试用例确保了业务逻辑的正确性和可靠性。后续可在权限继承、动态策略引擎等方面进一步增强。 ## 附录:最佳实践与常见场景 - 角色设计 - 角色命名规范:按领域/岗位划分,避免过细粒度过多角色 - 角色继承:通过父角色聚合权限,子角色继承父角色权限集合 - 权限合并:用户最终权限为用户所有角色权限的并集 - **新增** 内置角色管理:合理使用builtin标记区分系统角色和用户自定义角色 - 权限分配 - 菜单与按钮分离:菜单控制导航,按钮控制操作 - 最小权限原则:仅授予必要权限 - 定期审计:清理僵尸角色与冗余权限 - **新增** 祖先补全利用:前端只传叶子节点,后端自动补全父节点 - 用户-角色关联 - 批量赋权:导入模板批量分配 - 动态调整:在线修改即时生效(配合缓存刷新) - 数据范围 - 默认"本人",逐步放宽至"本部门""本部门及子部门""全部" - 跨部门协作:通过临时授权或共享数据表字段控制 - 动态权限 - 开关式权限:通过配置中心或数据库开关控制功能可见性 - 灰度发布:按租户/部门逐步开放新功能 - 缓存策略 - 键设计:user:perms:{userId}, role:menus:{roleId}, dept:tree:{deptId} - 失效策略:写后失效,定时全量重建 - 监控告警:命中率、延迟、内存占用监控 - 常见问题 - 权限不生效:检查缓存是否同步、注解是否遗漏 - 数据越权:核查数据范围拦截器是否生效 - 登录慢:检查第三方登录耗时、权限加载耗时 - **新增** 角色操作失败:检查内置角色保护、权限点是否存在、祖先补全逻辑 - **新增** 权限分配不完整:确认前端传递的节点ID是否正确,检查菜单层级关系 - **新增** 测试失败:检查Mock配置、断言逻辑、依赖注入 [本节为概念性总结,不直接分析具体文件]