# 权限管理系统 **本文引用的文件** - [PermissionResolver.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [ResourceNode.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/ResourceNode.java) - [MenuType.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [TreeUtils.java](file://crm-base/src/main/java/com/crm/base/utils/TreeUtils.java) - [DataInitializer.java](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java) - [ApiPermissionInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java) - [ApiPermissionCache.java](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java) - [ApiPermissionRule.java](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionRule.java) - [WebMvcConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java) - [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [ResourceServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java) ## 更新摘要 **所做更改** - **新增** 动态API权限拦截系统,基于Caffeine缓存和HandlerInterceptor实现URL级别的权限控制 - **增强** AuthServiceImpl认证流程重构,优化事务处理和第三方登录逻辑 - **完善** 资源树变更时的缓存失效机制,确保权限规则实时更新 - **扩展** 测试覆盖范围,包含API权限拦截、认证流程和数据权限的完整测试场景 ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件为基于RBAC(角色-资源)的权限管理系统文档,覆盖角色管理、菜单权限控制与数据权限范围控制。重点说明实体设计(如 SysRole、SysMenu)、权限校验逻辑(PermissionResolver三组件架构)、数据权限类型(DataScopeEnum),以及权限注解使用、动态权限加载与权限缓存机制的实现细节和配置方法。读者可据此快速理解系统权限模型并正确扩展与使用。 **更新** 本次更新重点关注新增的动态API权限拦截系统和改进的认证流程重构,包括ApiPermissionInterceptor、ApiPermissionCache等核心组件,以及AuthServiceImpl的事务处理优化。 ## 项目结构 本项目采用多模块组织: - crm-app:应用启动入口与全局配置 - crm-auth:认证与权限核心模块,包含RBAC实体、服务、安全拦截、第三方登录等 - crm-base:公共基础能力(异常、工具、通用配置、数据可见性上下文等) - crm-file:文件服务(与本权限文档无直接关联) ```mermaid graph TB subgraph "应用层" APP["crm-app
启动与应用配置"] end subgraph "认证与权限" AUTH["crm-auth
RBAC实体/服务/安全/客户端"] end subgraph "基础能力" BASE["crm-base
异常/工具/通用配置/数据可见性"] end APP --> AUTH AUTH --> BASE ``` 章节来源 - [CrmAppApplication.java:1-200](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java#L1-L200) - [AuthApplication.java:1-200](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java#L1-L200) ## 核心组件 - RBAC 实体 - SysRole:角色定义,承载角色标识、名称、状态等元信息 - SysMenu:增强的菜单与按钮资源定义,支持三层层级结构(目录-菜单-按钮)和细粒度权限控制 - SysUserRole、SysRoleMenu:用户-角色、角色-菜单的多对多关系表 - SysDept、SysUserDept:部门与用户部门关系,用于数据权限范围 - 数据传输对象 - ResourceNode:支持SysMenu与ResourceNode之间的双向转换,包含children字段支持层次结构 - 权限解析引擎(**重构**) - **PermissionResolver接口**:定义权限解析的核心抽象,包含resolve()和visibleMenuTree()两个方法 - **PermissionGrant值对象**:不可变的权限解析结果,包含数据可见性、权限码并集和角色编码 - **PermissionResolverImpl实现**:具体的权限解析逻辑实现,采用共享查询链优化 - **动态API权限拦截系统(新增)** - **ApiPermissionInterceptor**:HandlerInterceptor实现,基于AntPathMatcher进行URL模式匹配 - **ApiPermissionCache**:基于Caffeine的进程内缓存,懒加载已注册的API权限规则 - **ApiPermissionRule**:记录apiUrl pattern与perms权限码的映射关系 - 控制器层 - ResourceController:统一权限资源树管理端接口 - SystemController:系统管理相关接口,集成新的权限解析器 - 安全与拦截 - SecurityConfig、JwtAuthenticationFilter:认证流程与JWT过滤,集成新的权限解析器 - DataScopeInterceptor、DataScopeTables:数据权限拦截与SQL改写 - AuthLoginUser、TokenService:当前登录用户上下文与令牌操作 - 数据权限 - DataScopeEnum:数据权限范围枚举(如全部、本部门、本部门及子部门、仅本人等) - DataScope(注解)、DataScopeHelper、DataVisibilityContext:注解驱动的数据可见性控制 **更新** 权限解析引擎已重构为三组件架构,PermissionResolver接口定义了清晰的权限解析契约,PermissionGrant提供了不可变的权限结果封装,PermissionResolverImpl实现了高效的权限解析逻辑。新增的动态API权限拦截系统通过ApiPermissionInterceptor和ApiPermissionCache实现了URL级别的权限控制。SystemController现在直接使用PermissionResolver进行菜单树获取,简化了调用方式。 章节来源 - [SysRole.java:1-200](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java#L1-L200) - [SysMenu.java:12-76](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java#L12-L76) - [ResourceNode.java:16-84](file://crm-auth/src/main/java/com/crm/auth/domain/dto/ResourceNode.java#L16-L84) - [PermissionResolver.java:16-35](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java#L16-L35) - [PermissionGrant.java:23-67](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java#L23-L67) - [PermissionResolverImpl.java:42-139](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L42-L139) - [ApiPermissionInterceptor.java:32-82](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L32-L82) - [ApiPermissionCache.java:25-65](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L25-L65) - [SystemController.java:36-44](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java#L36-L44) ## 架构总览 下图展示认证与权限在请求链路中的关键交互:从JWT鉴权到权限校验、再到数据权限注入与SQL改写,以及新增的动态API权限拦截。 ```mermaid sequenceDiagram participant Client as "客户端" participant Filter as "JwtAuthenticationFilter" participant Interceptor as "ApiPermissionInterceptor" participant Resolver as "PermissionResolver" participant Grant as "PermissionGrant" participant Controller as "SystemController" participant DB as "数据库" Client->>Filter : "HTTP 请求 (带JWT)" Filter->>Filter : "验证Token" Filter->>Interceptor : "API权限拦截检查" Interceptor->>Cache : "获取API权限规则" Cache-->>Interceptor : "返回规则列表" Interceptor->>Interceptor : "AntPathMatcher匹配" Interceptor-->>Filter : "权限检查通过" Filter->>Resolver : "调用权限解析器" Resolver->>DB : "查询用户角色和权限" DB-->>Resolver : "返回权限数据" Resolver-->>Filter : "返回PermissionGrant" Filter->>Filter : "设置Security上下文" Controller->>Resolver : "获取菜单树" Resolver->>DB : "查询可见菜单" DB-->>Resolver : "返回菜单数据" Resolver-->>Controller : "返回菜单树" Controller-->>Client : "响应菜单数据" ``` **更新** 新的架构中,JwtAuthenticationFilter直接使用PermissionResolver进行权限解析,通过PermissionGrant封装完整的权限信息。新增的ApiPermissionInterceptor在JWT过滤器之后执行,提供URL级别的权限控制。SystemController也直接使用PermissionResolver获取菜单树,简化了调用链路。 图表来源 - [SecurityConfig.java:1-200](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L1-L200) - [JwtAuthenticationFilter.java:32-56](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L32-L56) - [ApiPermissionInterceptor.java:38-80](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L38-L80) - [PermissionResolver.java:16-35](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java#L16-L35) - [SystemController.java:40-44](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java#L40-L44) ## 详细组件分析 ### 权限解析三组件架构 **重构** 权限解析引擎已重构为清晰的三组件架构: #### PermissionResolver接口 定义了权限解析的核心抽象: - `resolve(Long userId)`:解析用户的数据可见性与全部授权码 - `visibleMenuTree(Long userId)`:获取用户可见的菜单树 #### PermissionGrant值对象 不可变的权限解析结果封装: - `DataVisibility visibility`:数据可见性信息 - `Set permCodes`:权限码并集(button节点且status=enabled) - `Set roleCodes`:角色编码集合 - `asAuthorities()`:合成Spring Security authorities #### PermissionResolverImpl实现 具体的权限解析逻辑实现: - 共享查询链:用户→角色→授权→资源 - 多角色权限合并:取最宽档可见性 - 权限码并集:仅包含button节点且启用状态的perms - 菜单树构建:过滤button节点并按sort排序 ```mermaid classDiagram class PermissionResolver { +resolve(userId) PermissionGrant +visibleMenuTree(userId) SysMenu[] } class PermissionGrant { +DataVisibility visibility +Set~String~ permCodes +Set~String~ roleCodes +asAuthorities() SimpleGrantedAuthority[] } class PermissionResolverImpl { -SysUserRoleMapper sysUserRoleMapper -SysRoleMapper sysRoleMapper -SysRoleMenuMapper sysRoleMenuMapper -SysMenuMapper sysMenuMapper -AuthUserMapper authUserMapper -DeptTreeCache deptTreeCache -SysUserDeptMapper sysUserDeptMapper +resolve(userId) PermissionGrant +visibleMenuTree(userId) SysMenu[] } PermissionResolver <|.. PermissionResolverImpl PermissionResolverImpl --> PermissionGrant : 返回 ``` **更新** 新的三组件架构提供了清晰的职责分离,PermissionResolver接口定义了明确的契约,PermissionGrant作为不可变值对象确保数据安全,PermissionResolverImpl专注于权限解析逻辑的实现。 图表来源 - [PermissionResolver.java:16-35](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java#L16-L35) - [PermissionGrant.java:23-67](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java#L23-L67) - [PermissionResolverImpl.java:42-139](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L42-L139) ### 动态API权限拦截系统(新增) **新增** 动态API权限拦截系统基于HandlerInterceptor实现URL级别的权限控制: #### ApiPermissionInterceptor HandlerInterceptor实现,负责URL模式匹配和权限校验: - **fail-open策略**:未匹配任何注册规则时放行请求 - **AntPathMatcher匹配**:支持*单层通配和**多层通配 - **权限检查**:从SecurityContext读取用户authority集合进行匹配 - **403响应**:匹配到规则但用户无对应权限时返回403 #### ApiPermissionCache 基于Caffeine的进程内缓存,懒加载API权限规则: - **单key缓存**:存储整份规则列表,避免频繁重建 - **懒加载机制**:首次访问时从数据库加载已启用的button节点 - **主动失效**:资源树变更后调用invalidate()清空缓存 - **规则过滤**:仅加载menuType=BUTTON且status='enabled'且有apiUrl的节点 #### ApiPermissionRule 简单的record类,记录API权限规则映射: - **pattern**:Ant风格的URL模式 - **perms**:对应的权限码 ```mermaid flowchart TD Start(["HTTP请求进入"]) --> GetRules["从缓存获取规则"] GetRules --> MatchCheck{"是否有匹配规则?"} MatchCheck --> |否| Pass["放行请求"] MatchCheck --> |是| CheckPerm["检查用户权限"] CheckPerm --> HasPerm{"用户有权限?"} HasPerm --> |是| Pass HasPerm --> |否| Deny["返回403"] Pass --> End(["继续处理"]) Deny --> End ``` **更新** 动态API权限拦截系统提供了灵活的URL级别权限控制,通过Caffeine缓存保证性能,fail-open策略确保向后兼容性。 图表来源 - [ApiPermissionInterceptor.java:38-80](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L38-L80) - [ApiPermissionCache.java:37-63](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L37-L63) - [ApiPermissionRule.java:9](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionRule.java#L9) ### WebMvcConfig拦截器注册 **新增** WebMvcConfig配置类负责注册API权限拦截器: - **拦截器注册**:将ApiPermissionInterceptor添加到MVC拦截器链 - **路径匹配**:仅拦截/api/**路径的请求 - **执行顺序**:在Spring Security(JWT过滤器)之后、Controller之前执行 ```mermaid flowchart TD Start(["请求进入"]) --> JwtFilter["JWT过滤器"] JwtFilter --> ApiInterceptor["API权限拦截器"] ApiInterceptor --> Controller["业务控制器"] Controller --> Response["返回响应"] ``` **更新** 拦截器的注册确保了权限检查的执行顺序,先进行JWT认证,再进行API权限检查,最后执行业务逻辑。 图表来源 - [WebMvcConfig.java:19-23](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L19-L23) ### JwtAuthenticationFilter集成新架构 **更新** JwtAuthenticationFilter已集成新的PermissionResolver: - **fail-closed安全原则**:权限解析失败即拒绝请求 - **一次解析完成**:通过PermissionResolver.resolve()一次性获取所有权限信息 - **自动上下文装载**:将DataVisibility装载到DataVisibilityContext - **Spring Security集成**:通过PermissionGrant.asAuthorities()生成authorities ```mermaid flowchart TD Start(["进入过滤器"]) --> TokenCheck{"Token有效?"} TokenCheck --> |否| Next["继续过滤器链"] TokenCheck --> |是| ResolvePerm["调用PermissionResolver.resolve()"] ResolvePerm --> LoadContext["装载DataVisibility到上下文"] LoadContext --> CreateAuth["创建UsernamePasswordAuthenticationToken"] CreateAuth --> SetContext["设置到SecurityContextHolder"] SetContext --> Next Next --> End(["结束"]) ``` **更新** 新的实现遵循fail-closed原则,权限解析失败时直接让请求失败,不存在部分成功的状态。通过PermissionGrant统一处理权限码和角色编码,简化了Spring Security的集成。 图表来源 - [JwtAuthenticationFilter.java:32-56](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L32-L56) ### SystemController集成新架构 **更新** SystemController已集成新的PermissionResolver: - **菜单树获取**:通过permissionResolver.visibleMenuTree(userId)直接获取菜单树 - **简化调用**:无需复杂的Service层调用,直接通过接口获取数据 - **类型安全**:返回类型为List,保证类型安全 ```mermaid flowchart TD Start(["菜单树请求"]) --> GetUserId["获取当前用户ID"] GetUserId --> CallResolver["调用PermissionResolver.visibleMenuTree()"] CallResolver --> QueryRoles["查询用户角色"] QueryRoles --> QueryMenus["查询角色授权的菜单"] QueryMenus --> FilterVisible["过滤可见菜单"] FilterVisible --> BuildTree["构建菜单树"] BuildTree --> Return["返回菜单树"] ``` **更新** SystemController现在直接使用PermissionResolver接口,简化了菜单树的获取逻辑,提高了代码的可维护性和测试性。 图表来源 - [SystemController.java:40-44](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java#L40-L44) ### 认证流程重构(AuthServiceImpl) **更新** AuthServiceImpl进行了重大重构,优化了第三方登录流程和事务处理: #### 主要改进 - **事务分离**:DB写入在事务中完成,Redis token写入在事务外执行,避免脏数据问题 - **组织准入预校验**:仅在首登注册前触发钉钉组织通讯录校验,减少HTTP调用开销 - **身份定位优化**:统一的locateUser方法供只读探测和登录定位共用,避免逻辑漂移 - **孤儿身份处理**:自动重定向指向已删除用户的身份绑定到新注册用户 #### 登录流程 1. **前置校验**:验证authCode非空 2. **三方用户信息获取**:通过clientFactory获取对应平台的用户信息 3. **组织准入预校验**:仅本地无匹配用户时触发 4. **事务内登录逻辑**:定位用户 + 校验状态 + 刷新登录时间 5. **签发token**:在事务外执行,失败不影响DB数据 ```mermaid flowchart TD Start(["登录请求"]) --> Validate["验证authCode"] Validate --> GetUserInfo["获取三方用户信息"] GetUserInfo --> PreCheck{"本地有匹配用户?"} PreCheck --> |是| SkipOrgCheck["跳过组织校验"] PreCheck --> |否| OrgCheck["组织准入预校验"] SkipOrgCheck --> DoLogin["事务内登录逻辑"] OrgCheck --> DoLogin DoLogin --> LocateUser["定位用户"] LocateUser --> CheckEnabled{"用户是否启用?"} CheckEnabled --> |否| ThrowError["抛出禁用错误"] CheckEnabled --> |是| UpdateLoginTime["刷新登录时间"] UpdateLoginTime --> CreateToken["签发token"] CreateToken --> ReturnResult["返回登录结果"] ``` **更新** 重构后的认证流程更加健壮,事务处理避免了脏数据,组织准入预校验提升了性能,身份定位逻辑更加清晰。 图表来源 - [AuthServiceImpl.java:64-93](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java#L64-L93) - [AuthServiceImpl.java:98-111](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java#L98-L111) - [AuthServiceImpl.java:117-139](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java#L117-L139) ### 三层资源层级结构设计 系统现在支持完整的三层资源层级结构,通过MenuType枚举定义了目录、菜单、按钮三种资源类型: - **目录(CATALOG)**:作为收纳容器,不承载页面,只能挂menu子节点 - **菜单(MENU)**:可导航的页面,只能挂button子节点 - **按钮(BUTTON)**:叶子节点,不可再挂子节点,承载权限码和接口映射 ```mermaid classDiagram class MenuType { +CATALOG : MenuType +MENU : MenuType +BUTTON : MenuType +fromCode(code) : MenuType +isLegalChild(parent, child) : boolean } class SysMenu { +Long parentId +String menuName +Integer menuType +String path +String component +String icon +Integer sort +Boolean visible +String description +String perms +String denyBehavior +String status +String apiUrl +SysMenu[] children } class ResourceNode { +Long id +Long parentId +String name +MenuType type +Integer sort +String description +String route +String perms +String denyBehavior +String apiUrl +String status +String icon +ResourceNode[] children +fromEntity(entity) : ResourceNode +toEntity() : SysMenu } MenuType <|-- SysMenu SysMenu <..> ResourceNode : 双向转换 ``` **更新** 三层资源层级结构在PermissionResolverImpl中得到了充分利用,visibleMenuTree方法会过滤掉button类型的菜单,只返回catalog和menu类型的菜单树。 图表来源 - [MenuType.java:12-52](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java#L12-L52) - [SysMenu.java:12-76](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java#L12-L76) - [ResourceNode.java:16-84](file://crm-auth/src/main/java/com/crm/auth/domain/dto/ResourceNode.java#L16-L84) ### SysMenu实体增强设计 SysMenu实体经过大幅增强,现在支持按钮级别的细粒度权限控制: - **基础字段**:parentId、menuName、menuType、path、component、icon、sort、visible - **按钮级权限字段**:description(描述/备注)、perms(权限码)、denyBehavior(拒绝行为)、status(权限点状态)、apiUrl(接口URL) - **树形结构支持**:children字段用于TreeUtils构建树时填充 **更新** SysMenu实体现在具备完整的按钮级权限控制能力,包括详细的权限描述、权限码、前端行为和API映射。 章节来源 - [SysMenu.java:12-76](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java#L12-L76) ### ResourceNode DTO层次结构支持 ResourceNode DTO现在支持完整的层次结构,包含children字段: - **层次结构字段**:`List children` - 子节点列表,仅在listAll()返回时填充 - **双向转换方法**: - `fromEntity方法`:将SysMenu实体转换为ResourceNode DTO - `toEntity方法`:将ResourceNode DTO转换回SysMenu实体 - **字段映射**:自动处理name↔menuName、route↔path、type↔menuType等字段映射 **更新** ResourceNode DTO现在支持层次结构,简化了前后端数据交互,提供了灵活的DTO转换机制和完整的树形数据支持。 章节来源 - [ResourceNode.java:16-84](file://crm-auth/src/main/java/com/crm/auth/domain/dto/ResourceNode.java#L16-L84) ### 统一资源树管理系统 完整的资源树管理系统,包含控制器、服务和接口,遵循全局接口契约: #### ResourceController 提供标准化的RESTful API接口,采用显式动作后缀设计: - `GET /api/resources/list`:**获取全量资源树(返回嵌套层次结构)** - `POST /api/resources/saveOrUpdate`:新增或编辑节点 - `POST /api/resources/delete`:删除节点 - `POST /api/resources/icon/upload`:图标上传 #### ResourceServiceImpl 实现完整的业务逻辑: - **层级约束验证**:确保父子关系的合法性,使用MenuType.isLegalChild()进行统一验证 - **字段必填校验**:根据不同类型验证必填字段 - **级联清理**:删除时自动清理角色授权引用 - **图标上传**:支持多种格式的图标文件上传 - **树形结构构建**:使用TreeUtils构建真正的层次结构 - **缓存失效**:保存和删除操作后调用apiPermissionCache.invalidate() ```mermaid flowchart TD Start(["保存资源节点"]) --> Validate["参数校验"] Validate --> TypeCheck{"检查节点类型"} TypeCheck --> |目录| CatalogRule["目录规则验证"] TypeCheck --> |菜单| MenuRule["菜单规则验证"] TypeCheck --> |按钮| ButtonRule["按钮规则验证"] CatalogRule --> HierarchyCheck["层级约束检查"] MenuRule --> HierarchyCheck ButtonRule --> HierarchyCheck HierarchyCheck --> ApplyMapping["DTO→Entity映射"] ApplyMapping --> Save["保存至数据库"] Save --> InvalidateCache["失效API权限缓存"] InvalidateCache --> Return["返回ResourceNode"] ``` **更新** 资源树管理系统提供了完善的CRUD操作和严格的业务规则验证。validateHierarchy方法现在使用MenuType.isLegalChild()进行层级验证,避免了硬编码的魔术数字,提高了代码的可维护性和可读性。新增的缓存失效机制确保权限规则实时更新。 图表来源 - [ResourceController.java:1-56](file://crm-auth/src/main/java/com/crm/auth/controller/ResourceController.java#L1-56) - [ResourceServiceImpl.java:55-97](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L55-L97) - [IResourceService.java:1-26](file://crm-auth/src/main/java/com/crm/auth/service/IResourceService.java#L1-L26) ### 数据权限范围控制(DataScopeEnum 与拦截器) - 数据权限类型 - 全部数据、本部门数据、本部门及子部门数据、仅本人数据等 - 注解驱动 - 在方法上使用 @DataScope 指定数据范围与目标表 - 拦截与SQL改写 - DataScopeInterceptor 读取上下文中的数据权限范围 - DataScopeTables 维护表与列的映射关系 - 通过 MyBatis Plus 的 SQL 注入器自动追加 WHERE 条件 ```mermaid sequenceDiagram participant Controller as "业务控制器" participant Interceptor as "DataScopeInterceptor" participant Helper as "DataScopeHelper" participant Tables as "DataScopeTables" participant MP as "MyBatisPlus注入器" participant DB as "数据库" Controller->>Interceptor : "进入受保护方法" Interceptor->>Helper : "解析@DataScope参数" Helper->>Tables : "查找表/列映射" Tables-->>Helper : "返回映射信息" Helper-->>MP : "生成数据权限WHERE片段" MP-->>DB : "执行带限制的SQL" DB-->>Controller : "返回受限结果" ``` 图表来源 - [DataScope.java:1-200](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java#L1-L200) - [DataScopeInterceptor.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java#L1-L200) - [DataScopeTables.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java#L1-L200) - [DataScopeHelper.java:1-200](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java#L1-L200) - [CrmSqlInjector.java:1-200](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java#L1-L200) 章节来源 - [DataScopeEnum.java:1-19](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java#L1-L19) - [DataScope.java:1-200](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java#L1-L200) - [DataScopeInterceptor.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java#L1-L200) - [DataScopeTables.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java#L1-L200) - [DataScopeHelper.java:1-200](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java#L1-L200) - [CrmSqlInjector.java:1-200](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java#L1-L200) ### 认证与安全配置(SecurityConfig、JwtAuthenticationFilter、TokenService) - SecurityConfig:定义URL白名单、授权规则、跨域与异常处理器 - JwtAuthenticationFilter:解析JWT,填充认证上下文,集成新的PermissionResolver - TokenService:生成、刷新、注销令牌,维护会话状态 ```mermaid classDiagram class SecurityConfig { +configure(http) void +cors() CorsConfigurationSource +exceptionHandler() SecurityExceptionHandlers } class JwtAuthenticationFilter { +doFilter(request, response, chain) void +parseToken(request) String +setContext(token) void +permissionResolver : PermissionResolver } class TokenService { +createToken(user) String +refreshToken(token) String +logout(token) void +validate(token) boolean } SecurityConfig --> JwtAuthenticationFilter : "注册过滤器" JwtAuthenticationFilter --> TokenService : "验证/解析令牌" JwtAuthenticationFilter --> PermissionResolver : "权限解析" ``` 图表来源 - [SecurityConfig.java:1-200](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L1-L200) - [JwtAuthenticationFilter.java:1-66](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L1-L66) - [TokenService.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java#L1-L200) 章节来源 - [SecurityConfig.java:1-200](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L1-L200) - [JwtAuthenticationFilter.java:1-66](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L1-L66) - [TokenService.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java#L1-L200) ### 权限注解使用与最佳实践 - 接口级权限控制 - 在需要权限校验的方法上标注权限标识(如菜单code或按钮标识) - 结合白名单跳过敏感接口校验 - 数据权限注解 - 在查询方法上标注 @DataScope,指定范围类型与目标表 - 确保 DataScopeTables 中已配置对应表的列映射 - 注意事项 - 避免在事务内频繁切换数据权限上下文 - 合理设置缓存失效时机,保证权限变更后即时生效 **更新** 基于新的PermissionResolver架构,权限控制更加模块化和安全化,fail-closed原则确保了权限解析失败的请求会被拒绝。新增的ApiPermissionInterceptor提供了URL级别的权限控制,与注解方式互补使用。 章节来源 - [DataScope.java:1-200](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java#L1-L200) - [DataScopeTables.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java#L1-L200) - [PermissionResolver.java:16-35](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java#L16-L35) ### 动态权限加载与缓存机制 - 动态加载 - 登录后根据用户角色拉取菜单树与按钮权限 - 将权限集合与菜单树写入缓存 - 缓存键设计 - 用户维度:user:{userId}:perms - 角色维度:role:{roleId}:menuTree - 缓存更新 - 角色/菜单变更触发失效 - 提供手动刷新接口供管理员使用 **更新** 新的PermissionResolverImpl实现了共享查询链优化,减少了重复查询,提高了性能表现。新增的ApiPermissionCache基于Caffeine提供进程内缓存,支持懒加载和主动失效。 章节来源 - [PermissionResolverImpl.java:118-139](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L118-L139) - [ApiPermissionCache.java:37-63](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L37-L63) - [SysMenuServiceImpl.java:1-200](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java#L1-L200) - [SysRoleServiceImpl.java:1-200](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java#L1-L200) ### 配置方法与初始化 - 权限配置 - PermissionConfig:集中管理权限相关开关与默认值 - 安全配置 - SecurityConfig:URL白名单、跨域、异常处理器 - 数据初始化 - DataInitializer:初始化默认角色、菜单、管理员账号等 **更新** DataInitializer现在支持三层资源层级结构的初始化,包括目录、菜单和按钮的完整权限体系。新增的WebMvcConfig负责注册API权限拦截器。 章节来源 - [PermissionConfig.java:1-200](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java#L1-L200) - [SecurityConfig.java:1-200](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L1-L200) - [DataInitializer.java:1-76](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java#L1-L76) - [WebMvcConfig.java:19-23](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L19-L23) ### 资源树层级约束验证机制 资源树的层级约束验证机制通过MenuType枚举和validateHierarchy方法实现: - **根节点验证**:根节点不允许添加button类型 - **父节点类型检查**:确保父节点存在且类型有效 - **层级关系验证**:使用MenuType.isLegalChild()方法进行统一的层级关系验证 - **空值安全处理**:防止未知菜单类型导致的NullPointerException ```mermaid flowchart TD Start(["验证层级关系"]) --> RootCheck{"是否根节点?"} RootCheck --> |是| RootRule["根节点规则检查"] RootCheck --> |否| ParentCheck["父节点存在性检查"] RootCheck --> |否| ParentTypeCheck["父节点类型检查"] ParentTypeCheck --> UnknownType{"父节点类型是否已知?"} UnknownType --> |否| Error["抛出未知类型错误"] UnknownType --> |是| LegalCheck["使用MenuType.isLegalChild验证"] LegalCheck --> Valid{"层级关系合法?"} Valid --> |是| Success["验证通过"] Valid --> |否| ErrorMsg["抛出具体错误消息"] RootRule --> Success ``` **更新** validateHierarchy方法现在完全依赖MenuType.isLegalChild()进行层级验证,消除了硬编码的魔术数字,提高了代码的可维护性和一致性。 图表来源 - [ResourceServiceImpl.java:174-202](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L174-L202) - [MenuType.java:37-51](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java#L37-L51) 章节来源 - [ResourceServiceImpl.java:174-202](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L174-L202) - [MenuType.java:37-51](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java#L37-L51) ### 路由路径处理逻辑改进 改进了路由路径处理逻辑,避免空字符串覆盖数据库值: - **非空值覆盖原则**:只有当node.getRoute()为非空时才覆盖entity.getPath() - **空串保留机制**:空字符串或空白字符不会覆盖数据库中已有的path值 - **编辑模式支持**:在编辑模式下,如果未提供route但数据库已有path则保持原值 这种改进确保了数据的一致性和完整性,避免了意外覆盖数据库中的有效数据。 章节来源 - [ResourceServiceImpl.java:243-246](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L243-L246) ### API设计标准化 资源管理系统的API设计遵循全局接口契约,采用显式动作后缀模式: - **GET /api/resources/list**:**获取全量资源树,返回嵌套层次结构** - **POST /api/resources/saveOrUpdate**:新增或编辑节点,支持表单字段接收 - **POST /api/resources/delete**:删除节点,通过@RequestParam接收ID参数 - **POST /api/resources/icon/upload**:图标上传,支持MultipartFile文件处理 这种设计确保了与应用程序其他部分的API设计一致性,避免了传统RESTful约定的歧义。 **更新** ResourceController严格遵循全局接口契约,所有写操作都使用POST + 动作后缀的模式,提升了API的一致性和可预测性。 章节来源 - [ResourceController.java:1-56](file://crm-auth/src/main/java/com/crm/auth/controller/ResourceController.java#L1-56) ### 树形结构构建机制 基于TreeUtils的高效树形结构构建机制: - **O(n)复杂度算法**:相比旧版O(n²)算法,性能显著提升 - **泛型支持**:支持Long/String等多种主键类型 - **灵活配置**:通过函数式接口支持自定义的getter/setter方法 - **智能分组**:按parentId分组后一次性挂载子节点 ```mermaid flowchart TD Start(["开始构建树"]) --> EmptyCheck{"列表是否为空?"} EmptyCheck --> |是| ReturnEmpty["返回空列表"] EmptyCheck --> |否| IdCollection["收集所有节点ID"] IdCollection --> GroupByParent["按parentId分组"] GroupByParent --> MountChildren["挂载子节点"] MountChildren --> IdentifyRoots["识别顶级节点"] IdentifyRoots --> ReturnTree["返回树形结构"] ``` **更新** TreeUtils工具类提供了高效的树形结构构建能力,PermissionResolverImpl中使用它来构建菜单树,ResourceServiceImpl中使用它来构建资源树。 图表来源 - [TreeUtils.java:36-71](file://crm-base/src/main/java/com/crm/base/utils/TreeUtils.java#L36-L71) - [PermissionResolverImpl.java:115](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L115) 章节来源 - [TreeUtils.java:1-91](file://crm-base/src/main/java/com/crm/base/utils/TreeUtils.java#L1-L91) - [PermissionResolverImpl.java:115](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L115) ## 依赖关系分析 - 模块依赖 - crm-auth 依赖 crm-base(异常、工具、数据可见性上下文) - crm-app 依赖 crm-auth(作为应用入口) - 组件耦合 - **PermissionResolverImpl** 依赖多个Mapper和DeptTreeCache - **JwtAuthenticationFilter** 依赖PermissionResolver和TokenService - **SystemController** 依赖PermissionResolver和其他服务 - **ApiPermissionInterceptor** 依赖ApiPermissionCache - **ResourceServiceImpl** 依赖ApiPermissionCache进行缓存失效 - DataScopeInterceptor 依赖 DataScopeHelper 与 CrmSqlInjector - SecurityConfig 依赖 JwtAuthenticationFilter 与异常处理器 ```mermaid graph LR APP["crm-app"] --> AUTH["crm-auth"] AUTH --> BASE["crm-base"] AUTH --> |"依赖"| Mapper["Mapper层"] AUTH --> |"依赖"| Service["Service层"] Service --> |"依赖"| Mapper ResourceController --> ResourceService ResourceService --> SysMenuService ResourceService --> FileApi ResourceService --> ApiPermissionCache JwtAuthenticationFilter --> PermissionResolver PermissionResolver --> PermissionResolverImpl SystemController --> PermissionResolver ApiPermissionInterceptor --> ApiPermissionCache ``` **更新** 新的架构中,PermissionResolverImpl解耦了具体的权限解析逻辑,JwtAuthenticationFilter和SystemController都通过PermissionResolver接口进行依赖,降低了耦合度。新增的ApiPermissionInterceptor依赖ApiPermissionCache,ResourceServiceImpl通过ApiPermissionCache实现缓存失效。 图表来源 - [CrmAppApplication.java:1-200](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java#L1-L200) - [AuthApplication.java:1-200](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java#L1-L200) 章节来源 - [CrmAppApplication.java:1-200](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java#L1-L200) - [AuthApplication.java:1-200](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java#L1-L200) ## 性能考虑 - 缓存优先 - 菜单树与权限集合应缓存至Redis,降低重复查询 - 合理设置过期时间与失效策略 - 批量与懒加载 - 菜单树构建尽量使用批量查询与懒加载 - 数据权限SQL优化 - 在 DataScopeTables 中精确映射表与列,避免全表扫描 - 为常用过滤列添加索引 - **新增** 资源树操作优化 - **TreeUtils提供O(n)复杂度的树构建算法,性能显著提升** - **listAll()方法直接返回树形结构,减少前端处理开销** - 图标上传异步处理提升用户体验 - 使用MenuType枚举提高类型检查效率 - **新增** API权限拦截优化 - **ApiPermissionCache基于Caffeine提供高性能进程内缓存** - **懒加载机制避免不必要的数据库查询** - **AntPathMatcher提供高效的URL模式匹配** **更新** 新的PermissionResolverImpl实现了共享查询链优化,通过authorizedMenus()方法复用查询结果,减少了重复的数据库访问。同时,PermissionGrant作为不可变值对象,避免了不必要的对象创建和修改开销。新增的ApiPermissionCache基于Caffeine提供高性能缓存,懒加载机制确保只在首次访问时加载规则。 ## 故障排查指南 - 常见错误 - 未认证或令牌过期:检查 JwtAuthenticationFilter 与 TokenService 配置 - 权限不足:确认角色-菜单授权是否正确,缓存是否及时失效 - 数据权限不生效:检查 @DataScope 注解与 DataScopeTables 映射 - **新增** 权限解析失败:检查PermissionResolverImpl的实现和依赖注入 - **新增** API权限拦截403:检查ApiPermissionCache规则和用户权限 - **新增** 认证流程异常:检查AuthServiceImpl的事务处理和第三方登录逻辑 - 定位步骤 - 查看全局异常处理器输出 - 开启调试日志,观察拦截器与SQL注入过程 - 核对缓存键与失效时机 - **新增** 检查PermissionResolverImpl中的权限解析逻辑 - **新增** 验证PermissionGrant的值对象构造和数据完整性 - **新增** 确认JwtAuthenticationFilter中的fail-closed逻辑 - **新增** 检查SystemController中PermissionResolver的使用 - **新增** 验证ApiPermissionInterceptor的URL匹配逻辑 - **新增** 检查ResourceServiceImpl中的缓存失效调用 **更新** 对于新的三组件架构,建议特别关注PermissionResolverImpl的依赖注入是否正确,PermissionGrant的构造参数是否完整,以及JwtAuthenticationFilter中的fail-closed逻辑是否正常工作。新增的API权限拦截系统需要检查ApiPermissionCache的规则加载和用户权限匹配。 章节来源 - [GlobalExceptionHandlerAdvice.java:1-200](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java#L1-L200) - [JwtAuthenticationFilter.java:1-66](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L1-L66) - [TokenService.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java#L1-L200) - [DataScopeInterceptor.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java#L1-L200) - [DataScopeTables.java:1-200](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java#L1-L200) - [PermissionResolverImpl.java:1-140](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L1-L140) - [ApiPermissionInterceptor.java:38-80](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L38-L80) - [AuthServiceImpl.java:64-93](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java#L64-L93) ## 结论 本权限管理系统以RBAC为核心,结合注解驱动的数据权限控制与拦截器实现的SQL改写,形成完整的权限校验与数据隔离方案。**通过本次重构,系统实现了PermissionResolver三组件架构,包括PermissionResolver接口、PermissionGrant不可变值对象和PermissionResolverImpl实现类,显著提升了权限解析的模块化设计和安全性。** **最重要的改进是新的三组件架构采用了fail-closed安全原则,确保权限解析失败的请求会被拒绝;PermissionGrant作为不可变值对象保证了数据的一致性和线程安全;PermissionResolverImpl实现了共享查询链优化,减少了重复查询开销。** 通过JwtAuthenticationFilter和SystemController对PermissionResolver的统一使用,简化了权限解析的调用方式,提高了代码的可维护性和测试性。 **新增的动态API权限拦截系统通过ApiPermissionInterceptor和ApiPermissionCache提供了URL级别的权限控制,基于Caffeine缓存保证性能,fail-open策略确保向后兼容性。** 资源树变更时自动触发缓存失效,确保权限规则实时更新。 **改进的认证流程重构优化了事务处理,避免了脏数据问题,组织准入预校验提升了性能,身份定位逻辑更加清晰。** 新的架构还充分利用了三层资源层级结构(目录-菜单-按钮),在visibleMenuTree方法中正确过滤button类型的菜单,只返回catalog和menu类型的菜单树。TreeUtils工具类的O(n)算法相比之前的O(n²)算法有显著的性能提升。通过对PermissionResolverImpl中authorizedMenus方法的优化,实现了用户→角色→授权→资源的共享查询链,进一步提升了性能表现。 通过合理的缓存策略与配置化开关,可在保障安全性的同时提升性能与可维护性。建议在生产环境严格管理权限变更与缓存失效,确保权限一致性与实时性。 ## 附录 - 术语 - RBAC:基于角色的访问控制 - 数据权限:基于用户所属组织或范围的查询限制 - fail-closed:失败关闭原则,权限解析失败即拒绝请求 - fail-open:失败开放原则,未匹配规则时放行请求 - 不可变值对象:一旦创建就不能修改的对象 - AntPathMatcher:Spring提供的URL模式匹配器 - 参考 - 实体与枚举:SysRole、SysMenu、DataScopeEnum、MenuType - 数据传输对象:ResourceNode - **新增** 权限解析三组件:PermissionResolver、PermissionGrant、PermissionResolverImpl - **新增** 动态API权限拦截:ApiPermissionInterceptor、ApiPermissionCache、ApiPermissionRule - 服务与拦截:DataScopeInterceptor、JwtAuthenticationFilter - **新增** 系统集成:SystemController对PermissionResolver的使用 - 工具类:TreeUtils - 配置与初始化:PermissionConfig、SecurityConfig、DataInitializer、WebMvcConfig **更新** 重点关注新的PermissionResolver三组件架构,它们提供了更强大的权限解析能力和更好的模块化设计。PermissionGrant不可变值对象确保了数据安全,PermissionResolverImpl的共享查询链优化提升了性能。SystemController对PermissionResolver的直接使用简化了调用方式,提高了代码的可维护性。新增的ApiPermissionInterceptor和ApiPermissionCache提供了灵活的URL级别权限控制,AuthServiceImpl的重构优化了认证流程。