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.
 
 
 
 
 
 

24 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) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.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) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [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) - [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/utils/SecurityUtils.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.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. 附录

简介

本文件围绕“自定义注解开发”展开,以 DataScope 数据权限注解为核心示例,系统讲解从注解定义、元注解使用、切面/拦截器实现到配置与集成的完整流程。文档面向不同技术背景的读者,既提供高层概览,也给出代码级分析与可视化图示,帮助开发者快速掌握在 Spring Boot + MyBatis-Plus 环境下实现数据权限控制的最佳实践。

项目结构

本项目采用多模块组织,数据权限相关能力主要分布在以下模块:

  • crm-base:基础能力层,包含通用注解、安全上下文、异常体系、工具类与 MyBatis-Plus 扩展配置。
  • crm-auth:认证授权模块,包含数据权限拦截器、权限配置、枚举定义以及集成测试。
  • 其他模块(如 crm-app、crm-file)通过依赖引入上述能力,并在业务接口上按需使用 DataScope 注解。
graph TB
subgraph "crm-base"
A["注解: DataScope"]
B["安全上下文: DataVisibilityContext"]
C["辅助工具: DataScopeHelper"]
D["异常: BusinessErrorException / PermissionErrorException"]
E["MyBatis-Plus 配置: MybatisPlusConfig / CrmSqlInjector"]
end
subgraph "crm-auth"
F["拦截器: DataScopeInterceptor"]
G["表映射: DataScopeTables"]
H["枚举: DataScopeEnum"]
I["配置: SecurityConfig / PermissionConfig"]
J["集成测试: DataScopeIntegrationTest"]
end
A --> F
F --> G
F --> H
F --> I
F --> E
F --> B
F --> C
D --> F
J --> F

图表来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java
  • SecurityConfig.java
  • PermissionConfig.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • MybatisPlusConfig.java
  • CrmSqlInjector.java
  • BusinessErrorException.java
  • PermissionErrorException.java
  • DataScopeIntegrationTest.java

章节来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java
  • SecurityConfig.java
  • PermissionConfig.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • MybatisPlusConfig.java
  • CrmSqlInjector.java
  • BusinessErrorException.java
  • PermissionErrorException.java
  • DataScopeIntegrationTest.java

核心组件

  • 注解定义:DataScope 用于在 Controller/Service 方法上声明数据范围策略与目标表字段映射等参数。
  • 拦截器:DataScopeInterceptor 负责解析注解、读取当前登录用户的数据权限级别、生成并注入 SQL 片段,最终影响查询结果集。
  • 表映射:DataScopeTables 维护表名与数据权限字段的映射关系,便于动态拼接 WHERE 条件。
  • 枚举:DataScopeEnum 定义数据范围类型(如全部、本部门、本人等)。
  • 安全上下文:DataVisibilityContext 与 LoginUser 承载当前请求的上下文信息(用户、部门、角色等)。
  • 辅助工具:DataScopeHelper 封装数据权限计算逻辑;SecurityUtils 提供便捷访问上下文的方法。
  • 配置:SecurityConfig/PermissionConfig 注册拦截器与权限规则;MybatisPlusConfig/CrmSqlInjector 将数据权限片段注入到 SQL 执行链路。
  • 异常:BusinessErrorException/PermissionErrorException 统一处理业务与权限异常。

章节来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • SecurityConfig.java
  • PermissionConfig.java
  • MybatisPlusConfig.java
  • CrmSqlInjector.java
  • BusinessErrorException.java
  • PermissionErrorException.java

架构总览

下图展示了 DataScope 注解驱动的数据权限控制整体流程:从请求进入,经拦截器解析注解与安全上下文,生成 SQL 片段,再由 MyBatis-Plus 注入到实际执行的 SQL 中,最终返回受数据范围限制的结果集。

sequenceDiagram
participant Client as "客户端"
participant Controller as "Controller/Service"
participant Interceptor as "DataScopeInterceptor"
participant Tables as "DataScopeTables"
participant Context as "DataVisibilityContext"
participant Helper as "DataScopeHelper"
participant MP as "MyBatis-Plus"
participant DB as "数据库"
Client->>Controller : "发起请求"
Controller->>Interceptor : "被注解 @DataScope 标记的方法调用"
Interceptor->>Context : "获取当前登录用户与权限级别"
Interceptor->>Tables : "解析目标表与字段映射"
Interceptor->>Helper : "根据策略生成数据权限片段"
Helper-->>Interceptor : "返回片段"
Interceptor->>MP : "将片段注入到 SQL 执行链"
MP->>DB : "执行带权限片段的 SQL"
DB-->>MP : "返回受限结果集"
MP-->>Controller : "返回结果"
Controller-->>Client : "响应数据"

图表来源

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

详细组件分析

注解定义与元注解使用

  • DataScope 注解通常包含以下关键元素:
    • 数据范围类型(对应 DataScopeEnum)
    • 目标表或实体映射(由 DataScopeTables 管理)
    • 可选的字段过滤键(如 deptId、userId)
    • 是否启用强制校验、是否忽略空值等开关
  • 元注解使用建议:
    • 使用 @Target(ElementType.METHOD) 限定在方法级别生效
    • 使用 @Retention(RetentionPolicy.RUNTIME) 保证运行时可解析
    • 使用 @Documented 便于 API 文档生成
  • 最佳实践:
    • 为注解属性设置默认值,降低使用成本
    • 对必填属性进行运行时校验,避免误用导致权限失效
    • 结合枚举约束取值,减少非法输入

章节来源

  • DataScope.java
  • DataScopeEnum.java

拦截器与切面实现原理

  • DataScopeInterceptor 的核心职责:
    • 解析方法上的 @DataScope 注解
    • 从 DataVisibilityContext 获取当前用户与权限级别
    • 依据 DataScopeTables 与 DataScopeHelper 生成 SQL 片段
    • 将片段注入到 MyBatis-Plus 的 SQL 执行链路
  • 典型流程:
    • 进入拦截器前,确保已加载当前用户上下文
    • 若未标注 @DataScope,则跳过权限处理
    • 若标注但缺少必要参数,抛出权限异常
    • 根据策略拼接 WHERE 条件片段,交由 MP 执行
  • 注意事项:
    • 避免在拦截器中进行重 IO 操作
    • 对频繁调用的方法做缓存优化(如权限片段缓存)
    • 严格区分只读与写操作的权限策略

章节来源

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

表映射与 SQL 注入机制

  • DataScopeTables 维护表名与权限字段的映射,支持:
    • 单表映射:如 user -> dept_id, owner_id
    • 多表关联:在 JOIN 场景下指定各表的权限字段
  • MyBatis-Plus 注入点:
    • MybatisPlusConfig 启用插件或拦截器
    • CrmSqlInjector 在 SQL 构建阶段插入权限片段
  • 设计要点:
    • 映射配置应集中管理,便于维护
    • 针对复杂查询,优先在 Mapper XML 中显式声明权限字段
    • 避免过度依赖自动拼接,必要时回退到手写 SQL

章节来源

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

安全上下文与用户模型

  • DataVisibilityContext 提供线程安全的上下文存取:
    • 存储当前用户、部门、角色、租户等信息
    • 提供清理方法防止内存泄漏
  • LoginUser 承载用户基本信息与权限标识
  • 使用建议:
    • 在请求入口初始化上下文,在出口清理
    • 避免在上下文中存放大对象
    • 对敏感信息进行脱敏处理

章节来源

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

配置与注册

  • SecurityConfig/PermissionConfig 负责:
    • 注册 DataScopeInterceptor
    • 配置拦截路径与排除路径
    • 设定默认数据范围策略
  • 建议:
    • 将白名单与黑名单配置化,便于环境差异化管理
    • 为不同环境提供不同的默认策略

章节来源

  • SecurityConfig.java
  • PermissionConfig.java

异常处理与参数验证

  • 统一异常体系:
    • BusinessErrorException:业务错误
    • PermissionErrorException:权限错误
  • 参数验证:
    • 在注解解析阶段校验必填项
    • 在拦截器中对上下文有效性进行检查
  • 建议:
    • 明确区分参数错误与权限错误,便于前端提示
    • 记录关键日志,便于问题定位

章节来源

  • BusinessErrorException.java
  • PermissionErrorException.java
  • GlobalExceptionHandlerAdvice.java

数据所有权与层级控制

  • DataOwnership 与 DataScopeLevel 用于表达数据所有权与层级关系:
    • 支持按个人、部门、公司等多层级范围
    • 与 DataScopeEnum 配合形成完整的权限矩阵
  • 使用建议:
    • 在建模时明确数据归属字段
    • 在查询时优先使用层级聚合,减少多次扫描

章节来源

  • DataOwnership.java
  • DataScopeLevel.java

集成测试与使用场景

  • DataScopeIntegrationTest 演示了如何:
    • 构造测试数据与上下文
    • 调用标注 @DataScope 的方法
    • 断言返回结果符合预期数据范围
  • TestOwnedData/TestOwnedDataMapper 作为示例实体与映射,展示如何在真实场景中应用数据权限

章节来源

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

依赖关系分析

下图展示了 DataScope 相关组件之间的依赖关系,包括注解、拦截器、表映射、枚举、上下文、配置与异常体系。

classDiagram
class DataScope {
+ "注解属性"
}
class DataScopeInterceptor {
+ "解析注解"
+ "生成SQL片段"
+ "注入执行链"
}
class DataScopeTables {
+ "表与字段映射"
}
class DataScopeEnum {
+ "数据范围类型"
}
class DataVisibilityContext {
+ "上下文存取"
}
class DataScopeHelper {
+ "权限计算"
}
class MybatisPlusConfig
class CrmSqlInjector
class SecurityConfig
class PermissionConfig
class BusinessErrorException
class PermissionErrorException
DataScope --> DataScopeInterceptor : "触发"
DataScopeInterceptor --> DataScopeTables : "读取映射"
DataScopeInterceptor --> DataScopeEnum : "匹配策略"
DataScopeInterceptor --> DataVisibilityContext : "获取上下文"
DataScopeInterceptor --> DataScopeHelper : "计算片段"
DataScopeInterceptor --> MybatisPlusConfig : "依赖配置"
DataScopeInterceptor --> CrmSqlInjector : "注入SQL"
DataScopeInterceptor --> SecurityConfig : "注册拦截器"
DataScopeInterceptor --> PermissionConfig : "权限规则"
DataScopeInterceptor --> BusinessErrorException : "业务异常"
DataScopeInterceptor --> PermissionErrorException : "权限异常"

图表来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • MybatisPlusConfig.java
  • CrmSqlInjector.java
  • SecurityConfig.java
  • PermissionConfig.java
  • BusinessErrorException.java
  • PermissionErrorException.java

章节来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScopeEnum.java
  • DataVisibilityContext.java
  • DataScopeHelper.java
  • MybatisPlusConfig.java
  • CrmSqlInjector.java
  • SecurityConfig.java
  • PermissionConfig.java
  • BusinessErrorException.java
  • PermissionErrorException.java

性能考虑

  • 缓存优化:
    • 对频繁使用的权限片段进行缓存,避免重复计算
    • 对表映射配置进行懒加载与热更新
  • 上下文管理:
    • 严格控制上下文生命周期,避免内存泄漏
    • 避免在上下文中存放大对象
  • SQL 优化:
    • 尽量在 WHERE 中使用索引友好的字段
    • 避免在权限片段中使用函数包裹字段
  • 并发安全:
    • 确保上下文与缓存的线程安全性
    • 合理设置超时与重试策略

[本节为通用指导,不直接分析具体文件]

故障排查指南

  • 常见问题:
    • 注解参数缺失导致权限失效:检查注解必填项与默认值
    • 上下文未初始化导致权限为空:确认请求入口是否正确设置上下文
    • SQL 片段未注入:检查 MyBatis-Plus 插件与注入点配置
    • 权限异常未捕获:确认全局异常处理器是否生效
  • 排查步骤:
    • 开启调试日志,观察拦截器执行轨迹
    • 打印生成的 SQL 片段,核对权限条件
    • 使用集成测试复现问题,逐步缩小范围

章节来源

  • GlobalExceptionHandlerAdvice.java
  • BusinessErrorException.java
  • PermissionErrorException.java
  • DataScopeIntegrationTest.java

结论

通过 DataScope 注解与拦截器的协同工作,项目实现了灵活、可扩展的数据权限控制。开发者只需在方法上标注注解,即可自动完成权限解析、SQL 注入与结果过滤。遵循本文的最佳实践,可在保证安全性的同时提升系统的可维护性与性能。

[本节为总结性内容,不直接分析具体文件]

附录

  • 自定义注解开发清单:
    • 明确注解用途与属性设计
    • 选择合适的元注解与保留策略
    • 编写解析与执行逻辑(拦截器/切面)
    • 配置注册与路径控制
    • 完善异常与日志
    • 编写单元测试与集成测试
  • 参考实现路径:
    • 注解定义:DataScope.java
    • 拦截器实现:DataScopeInterceptor.java
    • 表映射配置:DataScopeTables.java
    • 枚举定义:DataScopeEnum.java
    • 上下文与工具:DataVisibilityContext.java, DataScopeHelper.java
    • 配置与注入:SecurityConfig.java, PermissionConfig.java, MybatisPlusConfig.java, CrmSqlInjector.java
    • 异常体系:BusinessErrorException.java, PermissionErrorException.java, GlobalExceptionHandlerAdvice.java
    • 集成测试:DataScopeIntegrationTest.java