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
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)目录
简介
本文件围绕“自定义注解开发”展开,以 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