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.
25 KiB
25 KiB
安全与权限控制
**本文引用的文件** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.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) - [AuthLoginUser.java](file://crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [PermissionConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.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) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.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)目录
简介
本文件围绕 CRM 后端的安全与权限控制体系,系统性阐述数据权限注解 @DataScope、数据范围助手 DataScopeHelper、可见性上下文 DataVisibilityContext 的设计与使用;并说明登录用户信息 LoginUser、安全工具类 SecurityUtils 的功能。文档覆盖权限验证流程、数据隔离机制与安全最佳实践,帮助读者快速理解并正确使用该模块。
项目结构
安全与权限相关能力分布在两个模块:
- crm-base:提供通用安全基础能力(注解、上下文、工具类、实体基类等)
- crm-auth:提供认证授权实现(JWT 过滤器、令牌服务、权限配置、数据范围拦截器等)
graph TB
subgraph "crm-base"
A["注解: DataScope"]
B["上下文: DataVisibilityContext"]
C["助手: DataScopeHelper"]
D["用户: LoginUser"]
E["工具: SecurityUtils"]
F["实体基类: BaseEntity / OwnedEntity"]
G["SQL注入器: CrmSqlInjector"]
H["MyBatis Plus 配置: MybatisPlusConfig"]
end
subgraph "crm-auth"
I["认证过滤器: JwtAuthenticationFilter"]
J["令牌服务: TokenService"]
K["权限配置: PermissionConfig"]
L["数据范围拦截器: DataScopeInterceptor"]
M["数据范围表映射: DataScopeTables"]
N["枚举: DataScopeEnum"]
O["控制器: AuthController"]
P["服务接口: IAuthService"]
Q["登录用户扩展: AuthLoginUser"]
end
A --> C
B --> C
D --> E
E --> B
I --> J
J --> Q
K --> I
L --> C
L --> B
L --> M
L --> N
O --> P
P --> Q
图表来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- LoginUser.java
- SecurityUtils.java
- JwtAuthenticationFilter.java
- TokenService.java
- PermissionConfig.java
- DataScopeInterceptor.java
- DataScopeTables.java
- DataScopeEnum.java
- AuthController.java
- IAuthService.java
- AuthLoginUser.java
- BaseEntity.java
- OwnedEntity.java
- CrmSqlInjector.java
- MybatisPlusConfig.java
章节来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- LoginUser.java
- SecurityUtils.java
- JwtAuthenticationFilter.java
- TokenService.java
- PermissionConfig.java
- DataScopeInterceptor.java
- DataScopeTables.java
- DataScopeEnum.java
- AuthController.java
- IAuthService.java
- AuthLoginUser.java
- BaseEntity.java
- OwnedEntity.java
- CrmSqlInjector.java
- MybatisPlusConfig.java
核心组件
- 数据权限注解 @DataScope:用于在 Mapper/Service 层声明数据范围规则,驱动后续的数据过滤逻辑。
- 数据范围助手 DataScopeHelper:封装数据范围解析、条件拼装等能力,供业务代码或拦截器调用。
- 可见性上下文 DataVisibilityContext:线程级上下文,承载当前请求的可见范围、租户/部门/角色等信息,贯穿整个请求生命周期。
- 登录用户 LoginUser:抽象的用户主体模型,包含用户标识、组织归属、角色权限等关键信息。
- 安全工具类 SecurityUtils:提供获取当前登录用户、判断权限、构造查询条件等便捷方法。
- 认证与授权支撑:
- JwtAuthenticationFilter:解析 JWT 并建立安全上下文。
- TokenService:令牌签发、校验、刷新等。
- PermissionConfig:安全过滤链与跨域等配置。
- DataScopeInterceptor:在 SQL 执行前根据 @DataScope 和上下文动态注入 WHERE 条件。
- DataScopeTables:维护表名到数据范围字段的映射。
- DataScopeEnum:定义数据范围类型(如全部、本部门、本人等)。
章节来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- LoginUser.java
- SecurityUtils.java
- JwtAuthenticationFilter.java
- TokenService.java
- PermissionConfig.java
- DataScopeInterceptor.java
- DataScopeTables.java
- DataScopeEnum.java
架构总览
下图展示从 HTTP 请求进入,到认证、鉴权、数据范围注入的完整链路。
sequenceDiagram
participant Client as "客户端"
participant Filter as "JwtAuthenticationFilter"
participant Service as "IAuthService"
participant Token as "TokenService"
participant Context as "DataVisibilityContext"
participant Interceptor as "DataScopeInterceptor"
participant DB as "数据库"
Client->>Filter : "HTTP 请求(携带Token)"
Filter->>Token : "解析并校验Token"
Token-->>Filter : "返回用户信息(AuthLoginUser)"
Filter->>Context : "设置当前登录用户与可见范围"
Filter->>Service : "放行至业务服务"
Service->>Interceptor : "触发数据范围处理"
Interceptor->>Context : "读取可见范围与表映射"
Interceptor->>DB : "注入WHERE条件后执行SQL"
DB-->>Client : "返回受限结果集"
图表来源
- JwtAuthenticationFilter.java
- TokenService.java
- DataVisibilityContext.java
- DataScopeInterceptor.java
- DataScopeTables.java
- IAuthService.java
详细组件分析
数据权限注解 @DataScope
- 作用位置:通常标注在 Mapper 方法或 Service 方法上,声明该方法的数据范围策略。
- 关键语义:指定数据范围类型(如全部、本部门、本人)、关联字段(如 dept_id、owner_id)、是否强制等。
- 使用建议:
- 对涉及多租户/多部门的查询,务必标注 @DataScope,避免越权访问。
- 结合 OwnedEntity 的 owner_id 字段,可实现“仅本人”数据隔离。
- 对于导出/批量操作,谨慎开启“全部范围”,需二次确认权限。
章节来源
- DataScope.java
- DataScopeEnum.java
- OwnedEntity.java
数据范围助手 DataScopeHelper
- 职责:
- 解析 @DataScope 注解参数。
- 基于 DataVisibilityContext 中的用户与组织信息,生成对应的 WHERE 条件片段。
- 提供统一的条件拼装 API,便于在复杂查询中复用。
- 典型用法:
- 在 Service 层通过助手构建查询条件,或在自定义 SQL 中拼接范围条件。
- 与分页、排序组合时,确保范围条件优先于其他过滤条件。
章节来源
- DataScopeHelper.java
- DataVisibilityContext.java
可见性上下文 DataVisibilityContext
- 职责:
- 以 ThreadLocal 形式存储当前请求的可见范围、用户信息、租户/部门/角色等。
- 提供 set/get/clear 等方法,保证请求结束后清理上下文,避免内存泄漏。
- 设计要点:
- 在认证通过后立即填充上下文。
- 在请求结束或异常分支必须清理上下文。
- 对并发场景,确保上下文隔离,避免跨请求污染。
章节来源
- DataVisibilityContext.java
登录用户 LoginUser 与 AuthLoginUser
- LoginUser:抽象用户主体,包含用户ID、用户名、所属部门、角色集合等。
- AuthLoginUser:具体实现,由 TokenService 解析 JWT 后填充,作为安全上下文的权威来源。
- 使用建议:
- 所有鉴权逻辑应基于 LoginUser 进行,而非直接解析 Token。
- 敏感操作前校验用户状态、过期时间、角色权限。
章节来源
- LoginUser.java
- AuthLoginUser.java
- TokenService.java
安全工具类 SecurityUtils
- 功能:
- 获取当前登录用户(LoginUser)。
- 判断用户是否拥有某角色/菜单权限。
- 辅助构造数据范围条件(可委托 DataScopeHelper)。
- 使用建议:
- 在 Controller/Service 中优先使用 SecurityUtils 获取上下文,避免重复解析。
- 权限判断失败时应抛出明确的异常,便于全局异常处理。
章节来源
- SecurityUtils.java
- DataScopeHelper.java
认证与授权流程
- 认证流程:
- JwtAuthenticationFilter 拦截请求,解析 Token。
- TokenService 校验 Token 有效性,加载用户信息为 AuthLoginUser。
- 将用户信息写入 DataVisibilityContext,完成认证。
- 授权流程:
- PermissionConfig 配置白名单、拦截路径、角色/菜单权限校验。
- 业务方法可通过 SecurityUtils 进行细粒度权限判断。
- 数据范围通过 @DataScope + DataScopeInterceptor 自动注入 WHERE 条件。
flowchart TD
Start(["请求进入"]) --> Parse["解析Token"]
Parse --> Valid{"Token有效?"}
Valid --> |否| Deny["拒绝访问(未认证)"]
Valid --> |是| LoadUser["加载用户信息"]
LoadUser --> SetCtx["设置可见性上下文"]
SetCtx --> CheckPerm{"权限校验"}
CheckPerm --> |否| Deny
CheckPerm --> |是| ScopeCheck["@DataScope 数据范围处理"]
ScopeCheck --> Exec["执行业务SQL"]
Exec --> End(["返回结果"])
图表来源
- JwtAuthenticationFilter.java
- TokenService.java
- PermissionConfig.java
- DataScopeInterceptor.java
- DataScope.java
章节来源
- JwtAuthenticationFilter.java
- TokenService.java
- PermissionConfig.java
- DataScopeInterceptor.java
数据隔离机制
- 表级隔离:
- DataScopeTables 维护表名到数据范围字段的映射(如 dept_id、owner_id)。
- DataScopeInterceptor 在执行 SQL 前,根据映射与上下文注入 WHERE 条件。
- 行级隔离:
- 通过 OwnedEntity 的 owner_id 字段,实现“仅本人”数据隔离。
- 结合 DataScopeEnum 的“本人”范围,自动限制查询结果。
- 多层隔离:
- 支持“全部/本部门/本部门及子部门/本人”等多层级范围。
- 当存在多个范围注解时,按优先级合并条件,避免冲突。
classDiagram
class DataScope {
+范围类型
+关联字段
+是否强制
}
class DataScopeHelper {
+解析注解()
+生成条件()
+合并条件()
}
class DataVisibilityContext {
+用户信息
+可见范围
+set()/get()/clear()
}
class DataScopeInterceptor {
+拦截SQL()
+注入WHERE()
}
class DataScopeTables {
+表名->字段映射
}
class DataScopeEnum {
+全部
+本部门
+本部门及子部门
+本人
}
DataScope --> DataScopeHelper : "被解析"
DataScopeHelper --> DataVisibilityContext : "读取上下文"
DataScopeInterceptor --> DataScopeHelper : "调用助手"
DataScopeInterceptor --> DataScopeTables : "查映射"
DataScopeInterceptor --> DataScopeEnum : "匹配范围"
图表来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- DataScopeInterceptor.java
- DataScopeTables.java
- DataScopeEnum.java
章节来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- DataScopeInterceptor.java
- DataScopeTables.java
- DataScopeEnum.java
与 MyBatis Plus 的集成
- CrmSqlInjector:扩展 MP 的 SQL 注入器,支持在特定条件下注入范围条件。
- MybatisPlusConfig:注册拦截器与注入器,确保数据范围生效。
- 使用建议:
- 在需要自动注入范围的 Mapper 方法上标注 @DataScope。
- 对复杂查询,优先使用 DataScopeHelper 手动拼装条件,保证可控性。
章节来源
- CrmSqlInjector.java
- MybatisPlusConfig.java
依赖关系分析
- 低耦合高内聚:
- DataScopeHelper 专注条件拼装,不依赖 Web 容器。
- DataVisibilityContext 以 ThreadLocal 管理上下文,避免全局状态污染。
- DataScopeInterceptor 仅负责 SQL 拦截与注入,不关心业务细节。
- 外部依赖:
- JWT 解析与校验由 TokenService 提供。
- 权限配置由 PermissionConfig 集中管理。
- 数据库访问通过 MyBatis Plus 与自定义注入器协作。
graph LR
AuthController["AuthController"] --> IAuthService["IAuthService"]
IAuthService --> TokenService["TokenService"]
TokenService --> AuthLoginUser["AuthLoginUser"]
AuthLoginUser --> DataVisibilityContext["DataVisibilityContext"]
DataVisibilityContext --> DataScopeHelper["DataScopeHelper"]
DataScopeHelper --> DataScope["DataScope"]
DataScopeHelper --> DataScopeEnum["DataScopeEnum"]
DataScopeInterceptor["DataScopeInterceptor"] --> DataScopeHelper
DataScopeInterceptor --> DataScopeTables["DataScopeTables"]
DataScopeInterceptor --> CrmSqlInjector["CrmSqlInjector"]
图表来源
- AuthController.java
- IAuthService.java
- TokenService.java
- AuthLoginUser.java
- DataVisibilityContext.java
- DataScopeHelper.java
- DataScope.java
- DataScopeEnum.java
- DataScopeInterceptor.java
- DataScopeTables.java
- CrmSqlInjector.java
章节来源
- AuthController.java
- IAuthService.java
- TokenService.java
- AuthLoginUser.java
- DataVisibilityContext.java
- DataScopeHelper.java
- DataScope.java
- DataScopeEnum.java
- DataScopeInterceptor.java
- DataScopeTables.java
- CrmSqlInjector.java
性能考虑
- 上下文开销:DataVisibilityContext 使用 ThreadLocal,应避免在长任务或异步线程中误用,必要时显式传递。
- SQL 注入成本:DataScopeInterceptor 在每次 SQL 执行前注入条件,尽量复用条件片段,减少字符串拼接开销。
- 缓存策略:对频繁查询的部门树、角色权限等,可在服务层引入缓存(如 Redis),降低数据库压力。
- 批量操作:导出/导入时谨慎使用“全部范围”,必要时拆分批次并记录审计日志。
[本节为通用指导,无需引用具体文件]
故障排查指南
- 常见问题
- 未登录或 Token 失效:检查 JwtAuthenticationFilter 与 TokenService 的校验逻辑。
- 数据越权:确认 @DataScope 是否正确标注,DataScopeTables 映射是否完整。
- 上下文未清理:确保请求结束或异常分支调用 clear(),避免内存泄漏。
- 权限不足:检查 PermissionConfig 的路径白名单与角色/菜单权限配置。
- 定位步骤
- 打印 DataVisibilityContext 中的用户信息与可见范围。
- 查看 DataScopeInterceptor 注入的 WHERE 条件是否符合预期。
- 核对 DataScopeEnum 的范围类型与业务需求是否一致。
- 检查 OwnedEntity 的 owner_id 是否与当前用户匹配。
章节来源
- JwtAuthenticationFilter.java
- TokenService.java
- DataScopeInterceptor.java
- DataScopeTables.java
- DataVisibilityContext.java
- DataScopeEnum.java
- OwnedEntity.java
结论
本安全与权限控制体系通过注解驱动的 @DataScope、上下文隔离的 DataVisibilityContext、以及拦截器层面的 SQL 注入,实现了灵活且强约束的数据隔离与权限控制。配合 JWT 认证与 TokenService,形成从认证、鉴权到数据访问的全链路安全保障。遵循本文的最佳实践,可有效避免越权访问与数据泄露风险。
[本节为总结,无需引用具体文件]
附录
- 最佳实践清单
- 所有涉及多租户/多部门的查询必须标注 @DataScope。
- 使用 OwnedEntity 的 owner_id 实现“仅本人”隔离。
- 在认证通过后立即设置 DataVisibilityContext,并在请求结束时清理。
- 对敏感操作增加二次确认与审计日志。
- 定期审查 DataScopeTables 映射与权限配置,确保与业务演进同步。
[本节为补充信息,无需引用具体文件]