Elexvx
架构设计

数据模型与权限边界

理解多租户数据设计、RBAC、permission_key 与数据权限应该如何协同工作。

数据设计的基本原则

  • 统一基础字段
  • 租户隔离优先
  • 查询路径优先于“表看起来漂亮”
  • 审计、回滚、演进必须可支持

这几条原则看起来很基础,但它们直接决定了后面权限、审计、导出、分页、归档、跨租户隔离是不是会一路返工。

常见基础字段

核心表通常具备:

  • id
  • tenant_id
  • status
  • deletedis_deleted
  • created_at
  • updated_at
  • created_by
  • updated_by

很多核心表还会补:

  • version
  • remark
  • sort_order

主键建议走 bigint 的雪花 ID 或类似方案,避免长期依赖数据库自增。

多租户分层

典型分层包括:

  • 平台级公共数据
  • 租户级数据
  • 租户内组织级数据

这意味着“一个用户属于多个租户”是正常模型,不应把用户和租户绑定成一对一。

从开发角度看,这个分层会直接影响三件事:

  • 表是否必须带 tenant_id
  • 唯一键是全局唯一还是租户内唯一
  • 查询时需要按哪个边界做过滤

权限模型的核心目标

这套系统的权限模型,不只是为了“菜单能不能显示”,而是要同时解决:

  • 多租户用户关系
  • 平台态与租户态切换
  • 菜单、按钮、接口、数据范围联动
  • 审计与权限变更追踪

所以它不能退化成“用户-角色-菜单”三张表的简化版理解。

RBAC 的真实关系

推荐理解为:

用户 -> 用户租户关系 -> 角色 -> permission_key -> 页面/接口/数据范围

不要把“用户-角色-菜单”当成完整权限模型,它在多租户场景里不够。

更准确地说,用户进入某个租户之后,角色、菜单、按钮、数据范围才开始被解释。

permission_key 规范

统一格式:

<domain>:<resource>:<action>

例如:

  • system:user:view
  • system:file:upload
  • ai:knowledge:document:upload
  • plugin:management:install

这套 key 应被菜单、按钮、后端接口、审计和种子数据共同复用。

一个很重要的约束是:不要前端一套字符串、后端再写一套近似字符串。 一旦偏离,页面显隐、接口鉴权、角色授权、审计都很容易对不上。

权限四层

  1. 登录态:用户是否已认证
  2. 路由权限:是否能访问页面或 API 类别
  3. 操作权限:是否拥有某个 permission_key
  4. 数据权限:能看到哪些组织、资源和记录

这四层最容易混的是“按钮隐藏”和“真实权限校验”。前端隐藏按钮只是交互控制,真正安全边界仍然在后端接口和查询层。

前后端职责

前端负责:

  • 页面守卫
  • 菜单显隐
  • 按钮显隐

后端负责:

  • 真实接口鉴权
  • 数据范围过滤
  • 高风险操作审计

更细一点说:

  • auth-service 负责登录协议、token、刷新、二次认证
  • system-service IAM 负责角色、菜单、权限快照、数据范围基础
  • 业务 owner service 负责资源级权限和真正的数据边界

数据范围建议

第一阶段至少支持:

  • 仅本人
  • 本部门
  • 本部门及下级
  • 指定部门
  • 全租户

更复杂的场景可以继续扩展,但建议先把这五类做稳定。重点不是枚举写得多花,而是要把过滤真正落到后端查询层。

这些规则应该在后端 owner service 的查询层落地,而不是交给前端决定。

什么时候必须刷新权限快照

以下场景都应该触发权限快照失效或重建:

  • 用户角色变更
  • 角色菜单变更
  • 角色数据范围变更
  • 菜单状态变更
  • 用户加入或退出租户
  • 当前租户切换

开发红线

  • 不允许把前端按钮隐藏当成安全控制
  • 不允许让前端决定租户边界
  • 不允许不同模块各自定义一套权限编码
  • 不允许平台态无审计地做跨租户操作
  • 不允许用“是否管理员”粗暴替代细粒度权限

目录