# 数据权限控制 **本文引用的文件** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.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) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) - [VisibilityScope.java](file://crm-base/src/main/java/com/crm/base/security/VisibilityScope.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.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) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.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) - [PermissionResolver.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [SysDeptMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysDeptMapper.java) - [SysDeptServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysDeptServiceImpl.java) - [AuthUserMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/AuthUserMapper.java) - [AuthServiceImp.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [DataScopeIntegrationTest.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java) - [TestOwnedData.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedData.java) - [TestOwnedDataMapper.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedDataMapper.java) ## 更新摘要 **所做更改** - 新增权限解析系统集成章节,详细说明PermissionResolverImpl的统一权限计算逻辑 - 更新数据可见性上下文机制,反映新的DataVisibility和VisibilityScope设计 - 增强拦截器工作流程,体现与PermissionResolver的集成点 - 补充权限结果对象PermissionGrant的使用说明 - 更新架构总览图,展示新的权限解析流程 ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录:配置与使用示例](#附录配置与使用示例) ## 简介 本文件面向CRM系统的数据权限控制,围绕DataScope注解、DataScopeHelper工具类、DataScopeTables自动注入机制以及MyBatis拦截器DataScopeInterceptor展开,系统性阐述"全部数据、本部门数据、本部门及以下数据、仅本人数据"四类数据范围级别的实现原理、动态SQL生成策略、拦截器工作流与性能优化建议。文档同时介绍新的权限解析系统PermissionResolverImpl,该组件统一了所有权限计算逻辑,确保应用范围内权限计算的一致性。 ## 项目结构 数据权限相关代码分布在基础模块与认证模块中: - 基础模块(crm-base):定义数据权限注解、上下文、工具类、MyBatis Plus扩展(SQL注入器)。 - 认证模块(crm-auth):实现数据权限拦截器、表级元数据注册、枚举定义以及与部门/用户相关的查询服务。 ```mermaid graph TB subgraph "基础模块 crm-base" A["注解 DataScope"] B["级别 DataScopeLevel"] C["工具 DataScopeHelper"] D["上下文 DataVisibilityContext"] E["登录用户 LoginUser"] F["安全工具 SecurityUtils"] G["SQL注入器 CrmSqlInjector"] H["MyBatis配置 MybatisPlusConfig"] I["可见性 DataVisibility"] J["范围 VisibilityScope"] K["归属 DataOwnership"] end subgraph "认证模块 crm-auth" L["拦截器 DataScopeInterceptor"] M["表元数据 DataScopeTables"] N["枚举 DataScopeEnum"] O["部门服务 SysDeptServiceImpl"] P["部门映射 SysDeptMapper"] Q["用户映射 AuthUserMapper"] R["认证服务 AuthServiceImpl"] S["集成测试 DataScopeIntegrationTest"] T["权限解析 PermissionResolver"] U["权限解析实现 PermissionResolverImpl"] V["权限结果 PermissionGrant"] W["JWT过滤器 JwtAuthenticationFilter"] end A --> C B --> C C --> D D --> E F --> D G --> H I --> J I --> K L --> M L --> C L --> D L --> T O --> P R --> Q S --> L T --> U U --> V W --> U ``` **图表来源** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.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) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) - [VisibilityScope.java](file://crm-base/src/main/java/com/crm/base/security/VisibilityScope.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.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) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.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) - [PermissionResolver.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) ## 核心组件 - DataScope注解:用于在接口或方法上声明数据范围级别与目标表字段,驱动后续拦截与SQL改写。 - DataScopeLevel枚举:定义四种数据范围级别(全部数据、本部门数据、本部门及以下数据、仅本人数据)。 - DataScopeHelper:封装数据权限上下文设置、当前用户信息获取、部门树查询等通用逻辑。 - DataVisibilityContext:线程本地上下文,保存当前请求的数据可见性范围与用户信息。 - DataVisibility:不可变的数据可见性对象,包含用户ID、部门集合、数据范围档位及展开后的部门ID集合。 - VisibilityScope:可见范围抽象,将数据范围档位转换为具体的SQL过滤条件类型。 - DataOwnership:数据归属对象,表示新写入数据的归属人和主部门。 - LoginUser:登录用户模型,包含用户ID、所属部门ID等关键属性。 - SecurityUtils:从安全上下文提取当前登录用户的便捷工具。 - CrmSqlInjector:基于MyBatis Plus的SQL注入器,负责按规则向SQL追加数据权限条件。 - MybatisPlusConfig:注册MyBatis Plus插件与全局配置。 - DataScopeInterceptor:MyBatis拦截器,解析注解、构建并注入数据权限SQL片段。 - DataScopeTables:集中维护需要启用数据权限控制的表及其字段映射。 - DataScopeEnum:数据范围枚举(与级别对应),供拦截器与注入器统一判断。 - PermissionResolver:权限解析引擎接口,统一处理数据可见性、权限码并集、角色编码的计算。 - PermissionResolverImpl:权限解析引擎实现,整合所有权限计算逻辑。 - PermissionGrant:权限解析结果对象,包含数据可见性、权限码集合、角色编码集合。 - JwtAuthenticationFilter:JWT认证过滤器,集成权限解析并加载数据可见性上下文。 - 部门/用户服务与映射:为"本部门及以下"等范围提供部门树与用户归属查询支撑。 **章节来源** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.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) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) - [VisibilityScope.java](file://crm-base/src/main/java/com/crm/base/security/VisibilityScope.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.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) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.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) - [PermissionResolver.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) ## 架构总览 数据权限的整体流程如下: - JWT认证过滤器验证令牌后,调用PermissionResolver进行统一的权限解析。 - PermissionResolverImpl根据用户角色、部门关系计算数据可见性和权限码并集。 - 解析结果通过DataVisibilityContext加载到线程上下文中。 - MyBatis拦截器在执行SQL前从上下文读取可见范围,动态生成WHERE子句。 - 根据范围级别与表元数据,生成相应的SQL过滤条件(如部门ID IN 子查询、用户ID等于当前用户等)。 ```mermaid sequenceDiagram participant Client as "客户端" participant Filter as "JwtAuthenticationFilter" participant Resolver as "PermissionResolverImpl" participant Context as "DataVisibilityContext" participant Interceptor as "DataScopeInterceptor" participant DB as "数据库" Client->>Filter : "发送带Token的请求" Filter->>Resolver : "resolve(userId)" Resolver-->>Filter : "返回PermissionGrant" Filter->>Context : "load(visibility)" Filter-->>Client : "继续处理请求" Client->>Interceptor : "执行Mapper方法" Interceptor->>Context : "currentScope()" Context-->>Interceptor : "返回VisibilityScope" Interceptor->>DB : "执行带权限条件的SQL" DB-->>Client : "返回受限结果集" ``` **图表来源** - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) ## 详细组件分析 ### 权限解析系统集成 **更新** 新增权限解析系统,统一处理所有权限计算逻辑 - 设计要点 - PermissionResolver接口定义了统一的权限解析能力,包括数据可见性计算和菜单树生成。 - PermissionResolverImpl实现了完整的权限计算逻辑,整合用户角色、部门关系、授权资源等信息。 - PermissionGrant作为不可变的权限结果对象,包含数据可见性、权限码并集、角色编码集合。 - 采用fail-closed原则,解析失败直接抛出异常,不存在部分成功的状态。 - 核心流程 - 用户角色查询:根据userId查询关联的角色列表。 - 数据范围档位计算:多角色取最宽档,无角色时默认最窄档(仅本人)。 - 部门集合计算:主部门 + 兼职部门并集,支持实时查询兼职关系。 - 子树展开:对于"本部门及以下"档位,使用缓存的部门树进行子树展开。 - 权限码并集:收集所有授权按钮的perms,去重并排除停用权限点。 ```mermaid classDiagram class PermissionResolver { +resolve(userId) : PermissionGrant +visibleMenuTree(userId) : SysMenu[] } class PermissionResolverImpl { -SysUserRoleMapper sysUserRoleMapper -SysRoleMapper sysRoleMapper -SysRoleMenuMapper sysRoleMenuMapper -SysMenuMapper sysMenuMapper -AuthUserMapper authUserMapper -DeptTreeCache deptTreeCache -SysUserDeptMapper sysUserDeptMapper } class PermissionGrant { -DataVisibility visibility -Set~String~ permCodes -Set~String~ roleCodes +asAuthorities() : SimpleGrantedAuthority[] } class DataVisibility { -Long userId -Long primaryDeptId -Long[] deptIds -DataScopeLevel level -Long[] expandedDeptIds +visibilityScope() : VisibilityScope +dataOwnership() : DataOwnership } PermissionResolver <|.. PermissionResolverImpl PermissionResolverImpl --> PermissionGrant PermissionGrant --> DataVisibility ``` **图表来源** - [PermissionResolver.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) **章节来源** - [PermissionResolver.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolver.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) ### DataVisibilityContext与可见性管理 **更新** 简化了上下文管理机制,直接使用DataVisibility对象 - 职责 - 提供线程本地的数据可见性范围存储和访问。 - 由JwtAuthenticationFilter在认证通过后加载,请求结束时清理。 - 为数据权限拦截器和公共字段填充器提供统一的可见性访问接口。 - 关键特性 - 不可变性:DataVisibility对象构造成功后即完整可用,不存在中间状态。 - 安全性:无上下文时返回全可见或空归属,避免未认证请求的数据泄露。 - 异步支持:明确说明上下文不跨线程传递,异步任务需显式重新装入。 ```mermaid flowchart TD Start(["请求开始"]) --> Load["JwtAuthenticationFilter加载权限"] Load --> Resolve["PermissionResolverImpl解析权限"] Resolve --> CreateVis["创建DataVisibility对象"] CreateVis --> SetCtx["DataVisibilityContext.load()"] SetCtx --> Process["业务处理"] Process --> Clear["DataVisibilityContext.clear()"] Clear --> End(["请求结束"]) ``` **图表来源** - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) **章节来源** - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) - [VisibilityScope.java](file://crm-base/src/main/java/com/crm/base/security/VisibilityScope.java) ### DataScopeHelper工具类 - 职责 - 设置与清理数据可见性上下文(DataVisibilityContext)。 - 获取当前登录用户(LoginUser)及部门信息。 - 提供"本部门及以下"所需的部门树查询入口(通常委托服务层)。 - 维护表注解缓存,提高注解查找效率。 - 关键点 - 线程隔离:通过ThreadLocal保证并发安全。 - 容错处理:当无登录用户或上下文缺失时,降级为最小权限(仅本人或空结果)。 - 缓存优化:对部门树或用户部门信息进行短期缓存,减少重复查询。 - 跳过机制:支持临时跳过数据权限控制,用于特殊业务场景。 ```mermaid flowchart TD Start(["进入方法"]) --> CheckSkip{"shouldSkip()?"} CheckSkip --> |是| Skip["跳过权限检查"] CheckSkip --> |否| CheckCtx["检查上下文是否存在"] CheckCtx --> |存在| UseCtx["复用上下文中的范围与用户"] CheckCtx --> |不存在| LoadUser["加载当前登录用户"] LoadUser --> LoadDept["加载用户部门信息"] LoadDept --> BuildScope["计算数据范围级别"] BuildScope --> SetCtx["写入上下文"] SetCtx --> End(["结束"]) UseCtx --> End Skip --> End ``` **图表来源** - [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) - [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) - [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) ### DataScopeTables表元数据与自动注入 - 设计要点 - 集中维护需要数据权限控制的表名、主键/外键字段映射、是否启用权限开关。 - 支持按表维度开启/关闭权限,避免全量扫描带来的性能损耗。 - 自动注入机制 - 启动时由CrmSqlInjector读取DataScopeTables配置,建立表与字段的映射索引。 - 拦截器在执行SQL前,依据目标表查找映射,决定是否需要注入权限条件。 ```mermaid classDiagram class DataScopeTables { +表名映射 map +字段映射 fieldMap +是否启用 enabled +find(tableName) : DataScope } class CrmSqlInjector { +初始化映射 init() +注入SQL片段 inject() } DataScopeTables <.. CrmSqlInjector : "被读取与使用" ``` **图表来源** - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) **章节来源** - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) ### DataScopeInterceptor拦截器工作流程 **更新** 简化了拦截器逻辑,直接从DataVisibilityContext读取可见范围 - 触发时机 - MyBatis在执行SQL前拦截StatementHandler,解析@DataScope注解与上下文。 - 核心步骤 - 读取上下文:从DataVisibilityContext.currentScope()获取VisibilityScope。 - 快速判断:如果可见范围为ALL_VISIBLE,直接放行不解析SQL。 - SQL解析:使用JSQLParser解析原始SQL语句。 - 表匹配:从DataScopeTables查找目标表的注解配置。 - 条件生成:根据VisibilityScope.kind()生成相应的SQL条件表达式。 - 条件注入:将生成的条件注入到WHERE子句中。 - 执行SQL:放行至数据库执行。 ```mermaid sequenceDiagram participant MyBatis as "MyBatis" participant Interceptor as "DataScopeInterceptor" participant Context as "DataVisibilityContext" participant Tables as "DataScopeTables" participant Parser as "JSQLParser" MyBatis->>Interceptor : "拦截SQL执行" Interceptor->>Context : "currentScope()" Context-->>Interceptor : "返回VisibilityScope" alt ALL_VISIBLE Interceptor-->>MyBatis : "直接放行" else 需要过滤 Interceptor->>Tables : "find(tableName)" Tables-->>Interceptor : "返回DataScope注解" Interceptor->>Parser : "解析SQL" Parser-->>Interceptor : "返回AST" Interceptor->>Interceptor : "buildCondition(scope, anno)" Interceptor-->>MyBatis : "注入条件后继续执行" end ``` **图表来源** - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) **章节来源** - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) ### 数据范围级别实现逻辑 **更新** 新的实现通过PermissionResolverImpl统一计算,DataVisibilityContext提供最终可见范围 - 全部数据 - 不附加任何WHERE条件,直接执行原SQL。 - 对应VisibilityScope.Kind.ALL_VISIBLE。 - 本部门数据 - 生成dept_id = 当前用户部门ID的条件。 - 对应VisibilityScope.Kind.DEPARTMENTS,使用主部门ID集合。 - 本部门及以下数据 - 先查询当前用户部门的完整子树,生成dept_id IN (子树ID集合)的条件。 - 对应VisibilityScope.Kind.DEPARTMENTS,使用展开后的部门ID集合。 - 仅本人数据 - 生成owner_id = 当前用户ID或创建人字段=当前用户ID的条件。 - 对应VisibilityScope.Kind.OWNER。 - 一律不可见 - 生成永假条件(1=0),确保不返回任何数据。 - 对应VisibilityScope.Kind.NONE_VISIBLE。 ```mermaid flowchart TD Level["数据范围级别"] --> All["全部数据"] Level --> Dept["本部门数据"] Level --> DeptDown["本部门及以下数据"] Level --> Self["仅本人数据"] All --> AllVisible["VisibilityScope.ALL_VISIBLE"] Dept --> DeptVisible["VisibilityScope.DEPARTMENTS
deptIds=[主部门]"] DeptDown --> DeptDownVisible["VisibilityScope.DEPARTMENTS
deptIds=[子树展开]"] Self --> OwnerVisible["VisibilityScope.OWNER
ownerId=当前用户"] None["无部门且需部门过滤"] --> NoneVisible["VisibilityScope.NONE_VISIBLE"] ``` **图表来源** - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) - [VisibilityScope.java](file://crm-base/src/main/java/com/crm/base/security/VisibilityScope.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) **章节来源** - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [DataVisibility.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibility.java) - [VisibilityScope.java](file://crm-base/src/main/java/com/crm/base/security/VisibilityScope.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) ### 动态SQL生成与MyBatis拦截细节 **更新** 简化了SQL生成逻辑,直接使用VisibilityScope进行条件构建 - 动态SQL生成 - 根据VisibilityScope.kind()选择不同条件片段:OWNER、DEPARTMENTS、NONE_VISIBLE、ALL_VISIBLE。 - 字段名来源于DataScopeTables映射,确保与实体字段一致。 - 使用JSQLParser进行SQL AST操作,保持原有分页与排序不受影响。 - 拦截细节 - 在StatementHandler.prepare之前进行SQL改写。 - 保持原有分页与排序不受影响,仅在WHERE后追加条件。 - 异常保护:当上下文缺失或表未配置时,回退为最小权限或跳过注入。 - 性能优化:ALL_VISIBLE时直接放行,避免不必要的SQL解析。 ```mermaid flowchart TD Start(["开始"]) --> ReadScope["读取VisibilityScope"] ReadScope --> CheckKind{"kind() == ALL_VISIBLE?"} CheckKind --> |是| Skip["跳过注入"] CheckKind --> |否| ParseSQL["解析SQL为AST"] ParseSQL --> FindTable["查找表注解"] FindTable --> |未找到| Execute["执行原SQL"] FindTable --> |找到| BuildCond["buildCondition(scope, anno)"] BuildCond --> Inject["注入条件到WHERE"] Inject --> Execute Execute --> End(["结束"]) Skip --> Execute ``` **图表来源** - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) **章节来源** - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) ## 依赖关系分析 **更新** 新增了权限解析系统的依赖关系 - 组件耦合 - DataScopeInterceptor强依赖DataVisibilityContext与DataScopeTables,弱依赖JSQLParser。 - DataScopeHelper依赖DataVisibilityContext与SecurityUtils,间接依赖LoginUser。 - PermissionResolverImpl依赖多个Mapper和DeptTreeCache,实现复杂的权限计算逻辑。 - JwtAuthenticationFilter依赖PermissionResolver,负责加载数据可见性上下文。 - CrmSqlInjector依赖MybatisPlusConfig进行插件注册。 - 外部依赖 - MyBatis拦截器API、MyBatis Plus的SQL重写能力。 - JSQLParser用于SQL AST解析和操作。 - 部门与用户服务用于"本部门及以下"范围的子树查询。 ```mermaid graph LR Interceptor["DataScopeInterceptor"] --> Context["DataVisibilityContext"] Interceptor --> Tables["DataScopeTables"] Interceptor --> Parser["JSQLParser"] Helper --> Context Helper --> Utils["SecurityUtils"] Utils --> User["LoginUser"] Filter["JwtAuthenticationFilter"] --> Resolver["PermissionResolverImpl"] Resolver --> Mapper1["SysUserRoleMapper"] Resolver --> Mapper2["SysRoleMapper"] Resolver --> Mapper3["SysRoleMenuMapper"] Resolver --> Mapper4["SysMenuMapper"] Resolver --> Mapper5["AuthUserMapper"] Resolver --> Cache["DeptTreeCache"] Resolver --> Grant["PermissionGrant"] Grant --> Vis["DataVisibility"] Injector["CrmSqlInjector"] --> Config["MybatisPlusConfig"] ``` **图表来源** - [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) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.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) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [PermissionGrant.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionGrant.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) ## 性能考虑 **更新** 新增了权限解析系统的性能优化考虑 - 上下文缓存 - DataVisibilityContext在同一请求内复用,避免重复解析用户与部门信息。 - DataVisibility对象不可变,可安全地在多个组件间共享。 - 部门树缓存 - "本部门及以下"范围涉及的部门子树查询应加入缓存(如Redis或内存缓存),降低频繁树遍历开销。 - DeptTreeCache提供部门树的缓存和展开功能。 - 表级开关 - 通过DataScopeTables的enabled标志,按需启用数据权限,减少不必要的SQL改写。 - SQL片段复用 - 相同级别与字段的条件片段可缓存,避免每次生成字符串拼接。 - 权限解析缓存 - PermissionResolverImpl的结果可在请求级别缓存,避免重复计算。 - 分页与索引 - 确保dept_id、owner_id等字段有合适索引,避免IN子查询导致的全表扫描。 - 快速路径优化 - ALL_VISIBLE时直接放行,避免不必要的SQL解析和AST操作。 ## 故障排查指南 **更新** 新增了权限解析系统相关的故障排查 - 常见问题 - 未配置DataScopeTables导致注入失败:检查表映射是否注册。 - 上下文缺失导致权限降级:确认登录用户与安全上下文是否正确设置。 - 部门树查询缓慢:检查缓存与索引,必要时优化子树查询算法。 - 注解字段名不匹配:核对DataScope注解字段与实体字段一致性。 - 权限解析失败:检查PermissionResolverImpl的依赖注入和数据库连接。 - 数据可见性不正确:验证用户角色、部门关系和权限配置。 - 调试手段 - 打印拦截器日志,观察SQL改写前后差异。 - 使用集成测试验证不同级别下的结果集是否符合预期。 - 检查PermissionResolverImpl的日志输出,了解权限计算过程。 - 验证DataVisibilityContext中的可见性范围是否正确设置。 **章节来源** - [DataScopeIntegrationTest.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) ## 结论 该数据权限控制方案通过注解驱动的声明式配置、拦截器与SQL注入器的协同工作,实现了灵活且高效的数据范围控制。新的权限解析系统PermissionResolverImpl统一了所有权限计算逻辑,确保应用范围内权限计算的一致性。借助DataScopeTables的表级元数据管理,系统可在不影响业务代码的前提下,精准地为不同级别的数据访问注入过滤条件。结合上下文缓存与部门树缓存,整体性能可控,适用于多租户、多部门协作的CRM场景。 ## 附录:配置与使用示例 **更新** 新增了权限解析系统的配置和使用示例 - 配置示例 - 在DataScopeTables中注册需要数据权限控制的表与字段映射,并启用权限。 - 在MybatisPlusConfig中注册CrmSqlInjector与拦截器。 - 确保PermissionResolverImpl正确注入所有必要的依赖。 - 使用示例 - 在Service或Controller方法上添加@DataScope注解,指定级别与字段。 - 对于"本部门及以下"范围,确保部门服务提供子树查询能力。 - 利用PermissionResolverImpl进行统一的权限计算和验证。 - 测试示例 - 参考集成测试用例,验证不同级别下的数据过滤效果。 - 测试PermissionResolverImpl的各种权限组合场景。 - 验证DataVisibilityContext在不同请求间的隔离性。 **章节来源** - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeIntegrationTest.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/DataScopeIntegrationTest.java) - [TestOwnedData.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedData.java) - [TestOwnedDataMapper.java](file://crm-auth/src/test/java/com/crm/auth/security/scope/TestOwnedDataMapper.java) - [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java)