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.
 
 
 
 
 

23 KiB

数据所有权控制

**本文引用的文件** - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.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) - [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) - [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) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.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) - [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) - [SysUserDeptMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysUserDeptMapper.java) - [BaseServiceImpl.java](file://crm-base/src/main/java/com/crm/base/service/impl/BaseServiceImpl.java) - [CrmSqlInjector.java](file://crm-base/src/main/java/com/crm/base/config/CrmSqlInjector.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. 附录

简介

本文件面向数据所有权控制能力,系统性说明 DataOwnership 注解的使用方式与配置项、不同数据范围级别 DataScopeLevel 的定义与应用场景,并给出在 Service 层使用数据所有权注解的实践方法。文档还涵盖如何扩展新的数据范围级别、数据权限验证的拦截机制以及性能优化建议,帮助读者快速掌握并安全落地部门级、用户级和全局级的数据访问控制。

项目结构

本项目将数据所有权控制的核心定义放在基础模块 crm-base 中,具体实现与集成位于认证模块 crm-auth 中:

  • 基础能力(注解、上下文、工具)集中在 crm-base 的安全包下
  • 拦截器、表映射、枚举与实体模型集中在 crm-auth 的安全包与领域模型中
  • 测试用例覆盖端到端的数据范围校验流程
graph TB
subgraph "基础模块 crm-base"
A["DataOwnership 注解"]
B["DataScopeLevel 枚举"]
C["DataScopeHelper 工具"]
D["DataVisibilityContext 上下文"]
E["LoginUser 登录主体"]
F["SecurityUtils 安全工具"]
G["BaseServiceImpl 基类服务"]
H["CrmSqlInjector SQL注入器"]
end
subgraph "认证模块 crm-auth"
I["DataScopeInterceptor 拦截器"]
J["DataScopeTables 表映射"]
K["DataScopeEnum 数据范围枚举"]
L["AuthUser 用户实体"]
M["SysDept 部门实体"]
N["SysUserDept 用户-部门关联"]
O["AuthUserMapper / SysDeptMapper / SysUserDeptMapper"]
end
A --> C
B --> C
C --> D
D --> I
I --> J
I --> O
I --> K
L --> O
M --> O
N --> O
G --> H

图表来源

  • DataOwnership.java
  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • LoginUser.java
  • SecurityUtils.java
  • BaseServiceImpl.java
  • CrmSqlInjector.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java
  • AuthUser.java
  • SysDept.java
  • SysUserDept.java
  • AuthUserMapper.java
  • SysDeptMapper.java
  • SysUserDeptMapper.java

章节来源

  • DataOwnership.java
  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java

核心组件

  • DataOwnership 注解:用于标注 Service 方法或类,声明该方法的数据所有权策略与范围级别
  • DataScopeLevel:定义数据范围级别(如部门级、用户级、全局级等),决定查询时自动附加的过滤条件
  • DataScopeHelper:提供计算当前可见数据范围的辅助方法,结合上下文与用户信息生成过滤条件
  • DataVisibilityContext:线程局部上下文,承载当前请求的数据可见性范围、租户信息等
  • LoginUser 与 SecurityUtils:封装当前登录用户信息与获取方式
  • DataScopeInterceptor:拦截器,解析注解、读取上下文、组装 SQL 片段并注入到查询语句
  • DataScopeTables:维护表名与字段名的映射,确保 SQL 片段正确注入
  • BaseServiceImpl 与 CrmSqlInjector:在 MyBatis-Plus 层面统一注入数据范围条件

章节来源

  • DataOwnership.java
  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • LoginUser.java
  • SecurityUtils.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • BaseServiceImpl.java
  • CrmSqlInjector.java

架构总览

数据所有权控制的执行链路如下:

  • 调用方进入 Service 方法前,由 DataScopeInterceptor 解析 DataOwnership 注解
  • 根据 DataScopeLevel 与当前 LoginUser 的信息,通过 DataScopeHelper 计算可见范围
  • 将范围条件注入到 SQL 中(通过 CrmSqlInjector 与 DataScopeTables 映射)
  • 查询返回结果即已按数据范围过滤
sequenceDiagram
participant Client as "客户端"
participant Interceptor as "DataScopeInterceptor"
participant Context as "DataVisibilityContext"
participant Helper as "DataScopeHelper"
participant Mapper as "MyBatis Mapper"
participant DB as "数据库"
Client->>Interceptor : "进入受保护的方法"
Interceptor->>Interceptor : "解析 DataOwnership 注解"
Interceptor->>Context : "设置/读取可见范围上下文"
Interceptor->>Helper : "计算数据范围条件"
Helper-->>Interceptor : "返回过滤条件"
Interceptor->>Mapper : "注入 SQL 片段"
Mapper->>DB : "执行带条件的查询"
DB-->>Client : "返回已过滤的结果集"

图表来源

  • DataScopeInterceptor.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • CrmSqlInjector.java
  • DataScopeTables.java

详细组件分析

DataOwnership 注解与 DataScopeLevel 级别

  • DataOwnership 注解用于声明方法或类的数据所有权策略,包括范围级别、是否强制校验、是否忽略某些表等
  • DataScopeLevel 定义不同数据范围级别,常见包括:
    • 部门级:仅可见本部门及子部门数据
    • 用户级:仅可见本人数据
    • 全局级:可见全部数据(通常用于管理员)
  • 注解可配合自定义规则,例如指定特定字段作为归属标识(如 owner_id、dept_id)

章节来源

  • DataOwnership.java
  • DataScopeLevel.java

类图(注解与级别)

classDiagram
class DataOwnership {
+scopeLevel()
+forceCheck()
+ignoreTables()
+ownerField()
}
class DataScopeLevel {
<<enumeration>>
+DEPT
+USER
+GLOBAL
}
DataOwnership --> DataScopeLevel : "引用"

图表来源

  • DataOwnership.java
  • DataScopeLevel.java

上下文与工具:DataVisibilityContext、DataScopeHelper、LoginUser、SecurityUtils

  • DataVisibilityContext:保存当前请求的数据可见范围、租户 ID、操作人等上下文信息
  • DataScopeHelper:根据上下文与用户信息计算数据范围条件(如 dept_id IN (...) 或 owner_id = ?)
  • LoginUser:封装当前登录用户的基本信息(用户ID、部门ID、角色等)
  • SecurityUtils:提供从安全上下文中获取 LoginUser 的工具方法

章节来源

  • DataVisibilityContext.java
  • DataScopeHelper.java
  • LoginUser.java
  • SecurityUtils.java

类图(上下文与工具)

classDiagram
class DataVisibilityContext {
+setScope(scope)
+getScope()
+setTenantId(id)
+getTenantId()
}
class DataScopeHelper {
+computeScopeCondition(level, user)
+buildDeptFilter(deptIds)
+buildOwnerFilter(ownerId)
}
class LoginUser {
+userId
+deptId
+roles
}
class SecurityUtils {
+getCurrentUser()
}
DataScopeHelper --> DataVisibilityContext : "读写上下文"
DataScopeHelper --> LoginUser : "读取用户信息"
SecurityUtils --> LoginUser : "获取当前用户"

图表来源

  • DataVisibilityContext.java
  • DataScopeHelper.java
  • LoginUser.java
  • SecurityUtils.java

拦截器与表映射:DataScopeInterceptor、DataScopeTables

  • DataScopeInterceptor:在方法执行前解析 DataOwnership 注解,依据 DataScopeLevel 与用户信息计算过滤条件,并将条件注入到 SQL
  • DataScopeTables:维护表名与字段映射,确保 SQL 片段注入到正确的表与字段上

章节来源

  • DataScopeInterceptor.java
  • DataScopeTables.java

序列图(拦截流程)

sequenceDiagram
participant Svc as "Service方法"
participant Int as "DataScopeInterceptor"
participant Tbl as "DataScopeTables"
participant Sql as "SQL注入器"
Svc->>Int : "进入受保护方法"
Int->>Int : "解析 DataOwnership"
Int->>Tbl : "查找表字段映射"
Int->>Sql : "注入 WHERE 条件"
Sql-->>Svc : "返回带条件的查询"

图表来源

  • DataScopeInterceptor.java
  • DataScopeTables.java

数据范围枚举与实体模型:DataScopeEnum、AuthUser、SysDept、SysUserDept

  • DataScopeEnum:定义系统内数据范围枚举值,与 DataScopeLevel 协同使用
  • AuthUser:用户实体,包含用户基本信息与所属部门
  • SysDept:部门实体,支持层级结构
  • SysUserDept:用户与部门的关联表,用于多部门归属

章节来源

  • DataScopeEnum.java
  • AuthUser.java
  • SysDept.java
  • SysUserDept.java

类图(实体与枚举)

classDiagram
class DataScopeEnum {
<<enumeration>>
+DEPT
+USER
+GLOBAL
}
class AuthUser {
+id
+name
+deptId
}
class SysDept {
+id
+parentId
+name
}
class SysUserDept {
+userId
+deptId
}
AuthUser --> SysDept : "属于"
AuthUser --> SysUserDept : "关联"
SysDept --> SysUserDept : "被关联"

图表来源

  • DataScopeEnum.java
  • AuthUser.java
  • SysDept.java
  • SysUserDept.java

Service 层使用示例与扩展新级别

  • 在 Service 方法上使用 DataOwnership 注解,指定 DataScopeLevel 与必要参数(如 ownerField)
  • 若需新增数据范围级别,可在 DataScopeLevel 中添加枚举值,并在 DataScopeHelper 中补充对应计算逻辑
  • 建议在 BaseServiceImpl 中统一处理通用数据范围注入,减少重复代码

章节来源

  • BaseServiceImpl.java
  • DataScopeLevel.java
  • DataScopeHelper.java

流程图(扩展新级别)

flowchart TD
Start(["开始"]) --> AddEnum["在 DataScopeLevel 添加新级别"]
AddEnum --> UpdateHelper["在 DataScopeHelper 实现新级别的计算逻辑"]
UpdateHelper --> UpdateTables["在 DataScopeTables 配置新级别对应的表字段映射"]
UpdateTables --> Test["编写单元测试验证"]
Test --> End(["完成"])

图表来源

  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataScopeTables.java

数据权限验证的拦截机制

  • DataScopeInterceptor 负责解析注解、读取上下文、计算范围条件并注入 SQL
  • 若未检测到有效登录用户或范围级别不合法,应抛出权限异常
  • 可通过 CrmSqlInjector 统一注入 WHERE 条件,保证所有查询均受控

章节来源

  • DataScopeInterceptor.java
  • CrmSqlInjector.java

序列图(权限验证)

sequenceDiagram
participant Req as "请求"
participant Int as "DataScopeInterceptor"
participant Sec as "SecurityUtils"
participant Hlp as "DataScopeHelper"
participant Inj as "CrmSqlInjector"
Req->>Int : "进入方法"
Int->>Sec : "获取当前用户"
Sec-->>Int : "返回 LoginUser"
Int->>Hlp : "计算数据范围"
Hlp-->>Int : "返回过滤条件"
Int->>Inj : "注入 SQL 条件"
Inj-->>Req : "执行受控查询"

图表来源

  • DataScopeInterceptor.java
  • SecurityUtils.java
  • DataScopeHelper.java
  • CrmSqlInjector.java

依赖关系分析

  • DataOwnership 注解依赖 DataScopeLevel 枚举
  • DataScopeHelper 依赖 DataVisibilityContext、LoginUser 与安全工具
  • DataScopeInterceptor 依赖 DataScopeTables、DataScopeHelper 与 SQL 注入器
  • BaseServiceImpl 与 CrmSqlInjector 共同完成 MyBatis-Plus 层面的条件注入
graph LR
A["DataOwnership"] --> B["DataScopeLevel"]
C["DataScopeHelper"] --> D["DataVisibilityContext"]
C --> E["LoginUser"]
F["DataScopeInterceptor"] --> G["DataScopeTables"]
F --> C
F --> H["CrmSqlInjector"]
I["BaseServiceImpl"] --> H

图表来源

  • DataOwnership.java
  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • LoginUser.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • BaseServiceImpl.java
  • CrmSqlInjector.java

章节来源

  • DataOwnership.java
  • DataScopeLevel.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • LoginUser.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • BaseServiceImpl.java
  • CrmSqlInjector.java

性能考虑

  • 避免在每次请求中重复计算用户部门树:对部门层级进行缓存,减少递归查询
  • 合理设计 DataScopeTables 映射,避免不必要的表扫描;为常用过滤字段建立索引
  • 在 DataScopeHelper 中尽量使用集合批量查询,减少多次 IO
  • 对于大数据量查询,优先在数据库侧进行过滤,避免将大量数据拉取到内存再过滤
  • 使用分页查询并结合数据范围条件,降低单次查询压力

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

故障排查指南

  • 常见问题:
    • 未正确配置 DataScopeTables 导致 SQL 注入失败
    • 当前用户为空或权限不足导致范围计算异常
    • 数据范围级别与业务字段不匹配导致查询结果为空
  • 排查步骤:
    • 检查 DataOwnership 注解的参数是否正确
    • 确认 DataScopeLevel 与业务字段一致
    • 查看 DataScopeHelper 计算的过滤条件是否符合预期
    • 检查数据库索引与 SQL 执行计划

章节来源

  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeHelper.java

结论

数据所有权控制通过注解驱动与拦截器机制,实现了灵活且可扩展的数据范围管理。借助 DataOwnership 与 DataScopeLevel,开发者可以便捷地在 Service 层声明数据访问策略,并通过统一的拦截与注入机制保障数据安全。建议在生产环境中结合缓存、索引与分页等手段优化性能,同时完善测试覆盖以确保行为稳定。

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

附录

  • 端到端测试参考:
    • 数据范围集成测试:DataScopeIntegrationTest.java
    • 测试实体与映射:TestOwnedData.java、TestOwnedDataMapper.java

章节来源

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