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.
 
 
 
 
 

22 KiB

授权模型

**本文引用的文件** - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [ISysMenuService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysMenuService.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [MenuTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuTypeEnum.java) - [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.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) - [AuthLoginUser.java](file://crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.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) - [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) - [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) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.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)

目录

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

简介

本文件面向CRM系统的RBAC权限模型,围绕“角色-菜单-用户”三层权限架构进行系统化说明。重点覆盖:

  • 实体与表结构设计:SysRole(角色)、SysMenu(菜单)、SysUserRole(用户-角色关联)以及SysRoleMenu(角色-菜单关联)
  • 权限服务实现:PermissionServiceImpl的权限校验、菜单树获取、按钮级权限控制
  • 权限继承与数据权限范围:基于枚举的数据范围控制与拦截器注入
  • 动态权限加载:登录成功后从数据库加载并缓存到上下文
  • 使用示例:Controller层注解声明与调用流程

项目结构

本项目采用多模块组织,权限相关代码集中在认证授权模块crm-auth中,通用能力在crm-base中提供。关键路径如下:

  • 领域实体与枚举:crm-auth/domain/entity, crm-auth/domain/enums
  • 安全配置与过滤器:crm-auth/config, crm-auth/security
  • 服务实现:crm-auth/service/impl
  • 基础能力:crm-base/security, crm-base/annotation, crm-base/domain
graph TB
subgraph "认证授权模块(crm-auth)"
A["实体: SysRole/SysMenu/SysUserRole/SysRoleMenu"]
B["枚举: DataScopeEnum/MenuTypeEnum"]
C["服务: PermissionServiceImpl/SysMenuServiceImpl"]
D["安全: SecurityConfig/JwtAuthenticationFilter/TokenService"]
E["数据权限: DataScopeInterceptor/DataScopeTables"]
end
subgraph "基础模块(crm-base)"
F["注解: @DataScope"]
G["工具: DataScopeHelper/DataVisibilityContext/SecurityUtils"]
H["实体基类: BaseEntity/OwnedEntity"]
I["异常与结果: ResultCodeEnum/BusinessErrorException/PermissionErrorException"]
end
A --> C
B --> C
D --> C
E --> C
F --> E
G --> E
H --> A
I --> C

图表来源

  • SysRole.java
  • SysMenu.java
  • SysUserRole.java
  • SysRoleMenu.java
  • DataScopeEnum.java
  • MenuTypeEnum.java
  • PermissionServiceImpl.java
  • SecurityConfig.java
  • JwtAuthenticationFilter.java
  • TokenService.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataScope.java
  • DataScopeHelper.java
  • DataVisibilityContext.java
  • BaseEntity.java
  • OwnedEntity.java
  • ResultCodeEnum.java
  • BusinessErrorException.java
  • PermissionErrorException.java

章节来源

  • SecurityConfig.java
  • JwtAuthenticationFilter.java
  • TokenService.java

核心组件

  • 角色实体(SysRole):定义角色标识、名称、状态等元信息,作为权限集合的载体
  • 菜单实体(SysMenu):定义菜单层级、类型(目录/菜单/按钮)、路由与权限标识,支撑前端菜单渲染与按钮显隐
  • 用户角色关联(SysUserRole):将用户与角色建立多对多关系,承载用户的角色集合
  • 角色菜单关联(SysRoleMenu):将角色与菜单建立多对多关系,承载角色的菜单与按钮权限集合
  • 数据范围枚举(DataScopeEnum):定义数据可见范围(如全部、本部门、本人等),配合数据权限拦截器生效
  • 菜单类型枚举(MenuTypeEnum):区分目录、菜单、按钮三类资源,用于权限判断与前端渲染

章节来源

  • SysRole.java
  • SysMenu.java
  • SysUserRole.java
  • SysRoleMenu.java
  • DataScopeEnum.java
  • MenuTypeEnum.java

架构总览

下图展示了登录鉴权、权限加载、菜单渲染与数据权限控制的端到端流程。

sequenceDiagram
participant Client as "客户端"
participant Filter as "JwtAuthenticationFilter"
participant TokenSvc as "TokenService"
participant PermSvc as "PermissionServiceImpl"
participant MenuSvc as "SysMenuServiceImpl"
participant DB as "数据库"
participant Interceptor as "DataScopeInterceptor"
Client->>Filter : "携带JWT发起请求"
Filter->>TokenSvc : "解析并验证Token"
TokenSvc-->>Filter : "返回用户上下文"
Filter->>PermSvc : "加载用户权限(角色/菜单/按钮)"
PermSvc->>DB : "查询角色-菜单关联"
DB-->>PermSvc : "返回权限集合"
PermSvc-->>Filter : "写入当前用户权限上下文"
Client->>Interceptor : "访问受保护接口"
Interceptor->>DB : "根据@DataScope生成SQL条件"
DB-->>Interceptor : "返回受限数据集"
Interceptor-->>Client : "返回业务数据"

图表来源

  • JwtAuthenticationFilter.java
  • TokenService.java
  • PermissionServiceImpl.java
  • SysMenuServiceImpl.java
  • DataScopeInterceptor.java

详细组件分析

实体与表结构设计

  • SysRole(角色)
    • 关键字段:角色ID、角色编码、角色名称、状态、排序等
    • 作用:作为权限聚合的最小单元,多个角色可组合赋予用户
  • SysMenu(菜单)
    • 关键字段:菜单ID、父级ID、类型(目录/菜单/按钮)、路由、权限标识、图标、排序等
    • 作用:构建菜单树,同时为按钮级权限提供唯一标识
  • SysUserRole(用户-角色关联)
    • 关键字段:用户ID、角色ID
    • 作用:承载用户拥有的角色集合
  • SysRoleMenu(角色-菜单关联)
    • 关键字段:角色ID、菜单ID
    • 作用:承载角色拥有的菜单与按钮权限集合
erDiagram
SYS_ROLE {
bigint id PK
string role_code UK
string role_name
int status
int sort_order
}
SYS_MENU {
bigint id PK
bigint parent_id
enum type
string path
string permission_code
string icon
int sort_order
}
SYS_USER_ROLE {
bigint user_id FK
bigint role_id FK
}
SYS_ROLE_MENU {
bigint role_id FK
bigint menu_id FK
}
SYS_ROLE ||--o{ SYS_ROLE_MENU : "拥有"
SYS_MENU ||--o{ SYS_ROLE_MENU : "被拥有"
SYS_USER ||--o{ SYS_USER_ROLE : "属于"
SYS_ROLE ||--o{ SYS_USER_ROLE : "分配给"

图表来源

  • SysRole.java
  • SysMenu.java
  • SysUserRole.java
  • SysRoleMenu.java

章节来源

  • SysRole.java
  • SysMenu.java
  • SysUserRole.java
  • SysRoleMenu.java

权限服务实现(PermissionServiceImpl)

  • 权限校验逻辑
    • 基于当前登录用户已加载的角色集合,结合角色-菜单关联,计算用户具备的菜单与按钮权限集合
    • 支持按权限标识精确匹配,用于按钮级控制与接口级鉴权
  • 菜单权限获取
    • 通过菜单树结构组装,过滤出用户有权限的节点,形成前端可用的菜单树
    • 支持按类型筛选(目录/菜单/按钮),便于不同场景展示
  • 按钮级权限控制
    • 以权限标识为核心,前端根据标识控制按钮显示/隐藏,后端在关键操作处进行二次校验
  • 权限继承关系
    • 若菜单存在父子关系,父级菜单的权限通常包含子级;具体策略由菜单类型与枚举共同决定
  • 数据权限范围控制
    • 结合DataScopeEnum与@DataScope注解,在查询时自动注入部门/本人等数据范围条件
  • 动态权限加载机制
    • 登录成功后,从数据库读取用户角色与菜单权限,写入当前用户上下文,后续请求复用
flowchart TD
Start(["进入权限校验"]) --> LoadUser["加载当前用户上下文"]
LoadUser --> HasRoles{"是否拥有角色?"}
HasRoles -- 否 --> Deny["拒绝访问"]
HasRoles -- 是 --> LoadMenus["加载角色-菜单关联"]
LoadMenus --> BuildPerms["构建权限集合(菜单/按钮)"]
BuildPerms --> CheckTarget{"目标权限是否存在?"}
CheckTarget -- 否 --> Deny
CheckTarget -- 是 --> ApplyScope["应用数据权限范围"]
ApplyScope --> Allow["允许访问"]

图表来源

  • PermissionServiceImpl.java
  • DataScopeEnum.java
  • MenuTypeEnum.java

章节来源

  • PermissionServiceImpl.java
  • ISysMenuService.java
  • SysMenuServiceImpl.java

数据权限范围控制

  • 注解驱动:在Mapper或Service方法上使用@DataScope,指定数据范围级别
  • 拦截器注入:DataScopeInterceptor根据当前用户的数据范围,动态拼接SQL条件
  • 范围类型:参考DataScopeEnum,常见包括全部、本部门、本人等
  • 上下文传递:DataVisibilityContext保存当前可见范围,供查询阶段使用
sequenceDiagram
participant Controller as "控制器"
participant Mapper as "数据访问层"
participant Interceptor as "DataScopeInterceptor"
participant Context as "DataVisibilityContext"
participant DB as "数据库"
Controller->>Mapper : "调用带@DataScope的方法"
Mapper->>Interceptor : "触发数据权限拦截"
Interceptor->>Context : "读取当前用户数据范围"
Context-->>Interceptor : "返回范围参数"
Interceptor->>DB : "注入WHERE条件执行查询"
DB-->>Controller : "返回受限数据集"

图表来源

  • DataScope.java
  • DataScopeInterceptor.java
  • DataScopeTables.java
  • DataVisibilityContext.java

章节来源

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

动态权限加载机制

  • 登录成功后,TokenService解析用户身份并加载角色
  • PermissionServiceImpl根据角色集合查询菜单与按钮权限,构建权限集合
  • 权限集合写入当前用户上下文,后续请求无需重复查询
  • 前端根据权限集合渲染菜单与按钮
sequenceDiagram
participant Auth as "认证入口"
participant Token as "TokenService"
participant Perm as "PermissionServiceImpl"
participant DB as "数据库"
participant Ctx as "用户上下文"
Auth->>Token : "验证Token并获取用户信息"
Token-->>Auth : "返回用户基本信息"
Auth->>Perm : "加载用户权限"
Perm->>DB : "查询角色-菜单关联"
DB-->>Perm : "返回权限集合"
Perm->>Ctx : "写入权限集合"
Ctx-->>Auth : "权限可用"

图表来源

  • TokenService.java
  • PermissionServiceImpl.java
  • AuthLoginUser.java

章节来源

  • TokenService.java
  • PermissionServiceImpl.java
  • AuthLoginUser.java

在Controller层进行权限注解声明

  • 接口级鉴权:在需要鉴权的Controller方法上声明权限标识,由安全框架进行校验
  • 按钮级控制:前端根据权限标识控制按钮显示/隐藏;后端在关键业务点再次校验
  • 数据权限:在数据访问层方法上使用@DataScope,限定数据可见范围

使用建议:

  • 权限标识应遵循统一命名规范,确保前后端一致
  • 敏感操作需在后端进行二次校验,避免仅依赖前端控制
  • 数据权限应与业务实体字段对齐,保证查询条件正确注入

章节来源

  • SecurityConfig.java
  • JwtAuthenticationFilter.java
  • DataScope.java

依赖关系分析

  • 权限服务依赖菜单服务与实体映射,用于构建权限集合与菜单树
  • 安全过滤器依赖Token服务与权限服务,完成鉴权与权限加载
  • 数据权限拦截器依赖上下文与注解,动态注入查询条件
  • 基础模块提供通用实体基类、异常与结果封装
classDiagram
class PermissionServiceImpl {
+校验权限()
+获取菜单树()
+按钮级权限检查()
}
class SysMenuServiceImpl {
+构建菜单树()
+按类型过滤()
}
class DataScopeInterceptor {
+注入数据范围()
+处理注解()
}
class TokenService {
+解析Token()
+加载用户角色()
}
class JwtAuthenticationFilter {
+鉴权()
+加载权限()
}
PermissionServiceImpl --> SysMenuServiceImpl : "依赖"
JwtAuthenticationFilter --> TokenService : "依赖"
JwtAuthenticationFilter --> PermissionServiceImpl : "依赖"
DataScopeInterceptor --> PermissionServiceImpl : "读取权限上下文"

图表来源

  • PermissionServiceImpl.java
  • SysMenuServiceImpl.java
  • DataScopeInterceptor.java
  • TokenService.java
  • JwtAuthenticationFilter.java

章节来源

  • PermissionServiceImpl.java
  • SysMenuServiceImpl.java
  • DataScopeInterceptor.java
  • TokenService.java
  • JwtAuthenticationFilter.java

性能考虑

  • 权限集合缓存:登录成功后将权限集合写入上下文,避免重复查询
  • 菜单树构建优化:按需加载与缓存,减少前端渲染压力
  • 数据权限注入:在拦截器层面一次性注入条件,避免多次拼接
  • 索引设计:角色-菜单、用户-角色关联表建议建立复合索引以提升查询效率

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

故障排查指南

  • 权限校验失败
    • 检查用户是否分配了有效角色
    • 确认角色-菜单关联是否正确
    • 核对权限标识命名是否与前后端一致
  • 菜单渲染异常
    • 检查菜单类型与层级关系
    • 确认权限过滤逻辑是否正确
  • 数据权限不生效
    • 检查@DataScope注解是否正确使用
    • 确认数据范围枚举与上下文设置
  • 常见异常
    • 业务异常:BusinessErrorException
    • 权限异常:PermissionErrorException
    • 错误码:ResultCodeEnum

章节来源

  • BusinessErrorException.java
  • PermissionErrorException.java
  • ResultCodeEnum.java

结论

本RBAC权限模型通过角色-菜单-用户的三层架构,实现了灵活的权限控制与数据范围管理。PermissionServiceImpl作为核心服务,串联起鉴权、菜单渲染与按钮级控制;结合数据权限拦截器与上下文,实现了细粒度的数据可见性控制。建议在开发过程中严格遵循权限标识规范,并在关键业务点进行后端二次校验,以确保系统的安全性与一致性。

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

附录

  • 最佳实践
    • 权限标识命名规范化,前后端保持一致
    • 敏感操作必须后端校验,避免仅依赖前端控制
    • 数据权限与业务实体字段对齐,确保查询条件准确
  • 扩展建议
    • 引入权限变更事件,及时刷新上下文缓存
    • 增加权限审计日志,便于问题追踪与分析

[本节为补充内容,不直接分析具体文件]