You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

18 KiB

数据权限控制

**本文引用的文件** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.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) - [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) - [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) - [PermissionConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.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)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考虑
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本技术文档围绕数据权限控制模块展开,系统性阐述其设计理念与实现机制。重点覆盖:

  • @DataScope 注解的使用方式与语义
  • 数据范围级别(DataScopeLevel)的定义与扩展
  • SQL 自动注入机制(基于 MyBatis-Plus 的自定义注入器)
  • 数据权限拦截器的工作原理、表名映射规则与权限计算逻辑
  • 自定义数据权限规则的实现方法
  • 配置示例与调试技巧
  • 性能优化建议

该模块通过“注解声明 + 拦截器解析 + SQL 片段注入”的方式,在不侵入业务代码的前提下,对查询语句附加数据可见性条件,从而实现细粒度的数据隔离与访问控制。

项目结构

数据权限相关能力分布在基础模块与认证模块中:

  • 基础模块 crm-base:提供注解、上下文、工具类、SQL 注入器与 MyBatis-Plus 配置
  • 认证模块 crm-auth:提供数据权限拦截器、表名映射、权限配置与集成测试
graph TB
subgraph "基础模块 crm-base"
A["注解 DataScope"]
B["级别 DataScopeLevel"]
C["上下文 DataVisibilityContext"]
D["辅助 DataScopeHelper"]
E["SQL 注入 CrmSqlInjector"]
F["MyBatis-Plus 配置 MybatisPlusConfig"]
end
subgraph "认证模块 crm-auth"
G["拦截器 DataScopeInterceptor"]
H["表名映射 DataScopeTables"]
I["权限配置 PermissionConfig"]
J["集成测试 DataScopeIntegrationTest"]
end
A --> G
B --> G
C --> G
D --> G
E --> F
G --> H
G --> I
J --> G

图表来源

  • DataScope.java
  • DataScopeLevel.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • CrmSqlInjector.java
  • MybatisPlusConfig.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • PermissionConfig.java
  • DataScopeIntegrationTest.java

章节来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • CrmSqlInjector.java
  • MybatisPlusConfig.java
  • PermissionConfig.java
  • DataScopeIntegrationTest.java

核心组件

  • 注解 DataScope:用于在 Mapper/Service 层标注需要应用数据权限的方法或类,指定数据范围级别与可选的过滤字段等
  • 级别 DataScopeLevel:定义数据可见性的粒度,如全部、本部门、本部门及子部门、仅本人等
  • 上下文 DataVisibilityContext:线程级存储当前用户的数据可见性信息(如用户ID、部门ID、角色、数据范围级别等)
  • 辅助 DataScopeHelper:封装数据权限计算与上下文操作的工具方法
  • 注入器 CrmSqlInjector:基于 MyBatis-Plus 的 SQL 注入器,负责将数据权限条件以 WHERE 片段形式注入到查询语句
  • 拦截器 DataScopeInterceptor:拦截 MyBatis 执行链路,解析 @DataScope,结合上下文与表映射生成并注入权限条件
  • 表映射 DataScopeTables:维护实体/表名与数据权限字段的映射关系
  • 配置 PermissionConfig:启用/关闭数据权限、注册拦截器与注入器的开关与参数

章节来源

  • DataScope.java
  • DataScopeLevel.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • CrmSqlInjector.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • PermissionConfig.java

架构总览

数据权限的整体流程如下:

  • 业务调用入口在 Mapper/Service 上标注 @DataScope
  • MyBatis 执行 SQL 前,由 DataScopeInterceptor 拦截请求
  • 拦截器从 DataVisibilityContext 读取当前用户可见范围
  • 根据 DataScopeTables 的表映射,确定需要附加的权限条件
  • 通过 CrmSqlInjector 将权限条件注入为 SQL 片段
  • 最终执行带权限条件的 SQL,返回受限结果集
sequenceDiagram
participant Client as "客户端"
participant Service as "业务服务"
participant Mapper as "数据访问层"
participant Interceptor as "数据权限拦截器"
participant Tables as "表名映射"
participant Injector as "SQL 注入器"
participant DB as "数据库"
Client->>Service : "发起查询请求"
Service->>Mapper : "调用标注了@DataScope的方法"
Mapper->>Interceptor : "进入MyBatis执行链"
Interceptor->>Interceptor : "解析@DataScope与上下文"
Interceptor->>Tables : "获取表与权限字段映射"
Interceptor->>Injector : "生成并注入权限WHERE片段"
Injector-->>Interceptor : "返回拼接后的SQL"
Interceptor->>DB : "执行带权限条件的SQL"
DB-->>Interceptor : "返回结果集"
Interceptor-->>Mapper : "透传结果"
Mapper-->>Service : "返回受限数据"
Service-->>Client : "响应客户端"

图表来源

  • DataScopeInterceptor.java
  • DataScopeTables.java
  • CrmSqlInjector.java
  • MybatisPlusConfig.java

详细组件分析

注解 @DataScope 与数据范围级别

  • 作用位置:可作用于 Mapper 接口方法或类,表示该方法或该类下的所有方法受数据权限控制
  • 关键语义:
    • 数据范围级别:选择 DataScopeLevel 中的某一项,决定可见范围
    • 可选过滤字段:指定参与权限计算的列名(如 dept_id、owner_id)
    • 是否强制:某些场景下可跳过权限判断(谨慎使用)
  • 使用建议:
    • 在列表查询、导出、统计等涉及多行数据的场景必须标注
    • 对于单条记录查询,若已做资源级鉴权,可酌情不标注

章节来源

  • DataScope.java
  • DataScopeLevel.java

上下文与辅助工具

  • DataVisibilityContext:线程本地变量保存当前登录用户的可见范围信息,包括用户标识、部门、角色、数据范围级别等
  • DataScopeHelper:提供便捷方法,如设置/清除上下文、计算权限条件、合并多个条件等

章节来源

  • DataVisibilityContext.java
  • DataScopeHelper.java

表名映射规则

  • DataScopeTables 维护“表名 -> 权限字段”的映射,支持:
    • 默认映射:按约定(如 owner_id、dept_id)自动匹配
    • 自定义映射:显式配置特定表的权限字段
  • 映射优先级:
  • 注意事项:
    • 未映射的表不会注入权限条件,需补充映射或调整命名约定

章节来源

  • DataScopeTables.java

拦截器工作原理与权限计算

  • 触发时机:MyBatis 在执行 SQL 前,拦截器捕获请求元数据(方法签名、注解、参数)
  • 解析步骤:
    • 读取 @DataScope 注解与上下文可见范围
    • 根据表映射确定需要附加的条件字段
    • 依据数据范围级别生成对应的 WHERE 片段(如 IN 部门树、等于当前用户等)
    • 通过注入器将片段拼接到原始 SQL
  • 权限计算逻辑:
    • 全量:不附加条件
    • 本部门:dept_id IN (当前部门及其子部门集合)
    • 本部门及子部门:同上
    • 仅本人:owner_id = 当前用户ID
    • 自定义:可通过扩展点实现更复杂的条件

章节来源

  • DataScopeInterceptor.java
  • DataScopeLevel.java
  • DataScopeTables.java

SQL 自动注入机制

  • 基于 MyBatis-Plus 的自定义注入器 CrmSqlInjector:
    • 重写 SQL 构建过程,在 WHERE 子句插入权限条件
    • 保证不影响原有分页、排序、聚合等逻辑
  • 注入策略:
    • 优先使用 EXISTS 或 JOIN 的子查询(复杂关联时)
    • 简单场景直接追加 AND 条件
  • 配置项:
    • 是否启用 SQL 注入
    • 是否打印注入后的 SQL(便于调试)

章节来源

  • CrmSqlInjector.java
  • MybatisPlusConfig.java

配置与启用

  • PermissionConfig:集中管理数据权限开关、拦截器注册、注入器注册
  • 典型配置项:
    • 启用/禁用数据权限
    • 是否允许绕过权限(危险,慎用)
    • 日志级别与调试开关

章节来源

  • PermissionConfig.java

自定义数据权限规则

  • 扩展点:
    • 在 DataScopeLevel 中新增枚举值
    • 在拦截器中针对新级别实现条件生成逻辑
    • 在 DataScopeTables 中补充表映射
  • 最佳实践:
    • 保持条件幂等与可组合
    • 避免在高频路径中进行重型计算
    • 提供单元测试覆盖边界情况

章节来源

  • DataScopeLevel.java
  • DataScopeInterceptor.java
  • DataScopeTables.java

集成测试与验证

  • DataScopeIntegrationTest:端到端验证数据权限生效
  • TestOwnedData / TestOwnedDataMapper:模拟实体与数据访问,配合测试用例验证不同数据范围级别的查询结果

章节来源

  • DataScopeIntegrationTest.java
  • TestOwnedData.java
  • TestOwnedDataMapper.java

依赖关系分析

classDiagram
class DataScope {
+ "注解:标注数据权限"
}
class DataScopeLevel {
+ "枚举:数据范围级别"
}
class DataVisibilityContext {
+ "上下文:当前用户可见范围"
}
class DataScopeHelper {
+ "工具:权限计算与上下文操作"
}
class DataScopeInterceptor {
+ "拦截器:解析注解并注入条件"
}
class DataScopeTables {
+ "映射:表名与权限字段"
}
class CrmSqlInjector {
+ "注入器:SQL片段注入"
}
class MybatisPlusConfig {
+ "配置:注册注入器与拦截器"
}
class PermissionConfig {
+ "配置:启用/禁用数据权限"
}
DataScope --> DataScopeLevel : "使用"
DataScopeInterceptor --> DataScope : "解析"
DataScopeInterceptor --> DataScopeLevel : "计算"
DataScopeInterceptor --> DataVisibilityContext : "读取"
DataScopeInterceptor --> DataScopeTables : "查映射"
DataScopeInterceptor --> CrmSqlInjector : "调用注入"
MybatisPlusConfig --> CrmSqlInjector : "注册"
PermissionConfig --> DataScopeInterceptor : "启用"

图表来源

  • DataScope.java
  • DataScopeLevel.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • CrmSqlInjector.java
  • MybatisPlusConfig.java
  • PermissionConfig.java

章节来源

  • DataScopeInterceptor.java
  • CrmSqlInjector.java
  • MybatisPlusConfig.java
  • PermissionConfig.java

性能考虑

  • 减少不必要的上下文计算:仅在需要时加载部门树与角色信息,避免重复查询
  • 缓存热点数据:对部门树、用户角色等数据进行缓存,降低数据库压力
  • SQL 注入优化:
    • 尽量使用简单的 AND 条件,避免复杂子查询
    • 对高频查询的权限条件进行预编译与复用
  • 索引设计:确保权限字段(如 dept_id、owner_id)有合适的索引
  • 监控与限流:对数据权限相关的慢查询进行监控与告警

[本节为通用指导,无需列出具体文件来源]

故障排查指南

  • 常见问题:
    • 权限未生效:检查 @DataScope 是否正确标注、上下文是否设置、拦截器是否启用
    • 数据过多或过少:核对表映射是否正确、数据范围级别是否符合预期
    • SQL 注入异常:查看注入器日志,确认 SQL 拼接逻辑
  • 调试技巧:
    • 开启 SQL 注入日志,观察注入后的完整 SQL
    • 使用集成测试用例逐步验证不同数据范围级别的行为
    • 在 DataVisibilityContext 中打印当前用户可见范围,辅助定位问题

章节来源

  • DataScopeIntegrationTest.java
  • CrmSqlInjector.java
  • DataScopeInterceptor.java

结论

数据权限控制模块通过注解驱动与拦截器机制,实现了无侵入、可扩展、高性能的数据可见性控制。借助清晰的层次划分与完善的配置项,开发者可以灵活定制权限规则,并通过集成测试与调试手段保障系统稳定性。建议在业务中广泛采用该方案,以实现统一、可控的数据安全策略。

[本节为总结性内容,无需列出具体文件来源]

附录

  • 配置示例要点:
    • 在 PermissionConfig 中启用数据权限
    • 在 MybatisPlusConfig 中注册 CrmSqlInjector
    • 在 DataScopeTables 中补充业务表的权限字段映射
  • 使用示例要点:
    • 在 Mapper 方法上添加 @DataScope(level = DataScopeLevel.本部门, fields = {"dept_id"})
    • 确保 DataVisibilityContext 在请求开始时正确设置用户可见范围
  • 扩展示例要点:
    • 新增 DataScopeLevel 枚举值并在拦截器中实现对应逻辑
    • 在 DataScopeTables 中增加新表的映射

[本节为补充说明,无需列出具体文件来源]