基于 Go 技术栈的 多租户 RBAC 模型设计方案,涵盖完整的数据库设计、组织层级、数据范围控制以及审计与扩展体系。方案采用“共享数据库、tenant_id 隔离”,配合 Gin、GORM、PostgreSQL、Wire、Viper,确保统一隔离、高性能和易用性。

一、总体设计思路

1. 多租户策略

  • 共享数据库 + tenant_id 行级隔离
    所有业务表包含 tenant_id 列,查询时通过 GORM 全局作用域自动添加 WHERE tenant_id = ? 条件。
  • 租户上下文传递
    由 Gin 中间件从 JWT 中解析租户 ID 并注入 context.Context,GORM 通过 Context 回调动态获取租户 ID,保证每次查询天然隔离。
  • 高效性保障:为所有涉及租户的查询建立 (tenant_id, ...) 联合索引或赋予 tenant_id 单独索引,确保最优计划。

2. 技术选型分布

技术 用途
Golang 后端语言
Gin HTTP 框架,中间件链
GORM ORM,自动租户过滤、钩子
PostgreSQL 主数据库,支持数组、JSONB、物化路径/闭包表
Wire 依赖注入,组织 Service/Repo 层次
Viper 配置管理(数据库、JWT、租户)

二、数据库模型(ER 与核心表)

1. 租户与用户

-- 租户表
CREATE TABLE tenants (
    id          BIGSERIAL PRIMARY KEY,
    name        VARCHAR(100) NOT NULL,
    code        VARCHAR(50)  NOT NULL UNIQUE,
    status      SMALLINT DEFAULT 1,     -- 1:启用 0:禁用
    created_at  TIMESTAMP DEFAULT now(),
    updated_at  TIMESTAMP
);

-- 用户表
CREATE TABLE users (
    id            BIGSERIAL,
    tenant_id     BIGINT NOT NULL,
    username      VARCHAR(64) NOT NULL,
    password_hash VARCHAR(256) NOT NULL,
    email         VARCHAR(128),
    phone         VARCHAR(20),
    real_name     VARCHAR(64),
    avatar        VARCHAR(256),
    status        SMALLINT DEFAULT 1,
    org_id        BIGINT,                -- 主部门(组织)
    created_at    TIMESTAMP DEFAULT now(),
    updated_at    TIMESTAMP,
    PRIMARY KEY (id),
    CONSTRAINT uq_tenant_user UNIQUE (tenant_id, username)
);
CREATE INDEX idx_users_tenant ON users(tenant_id);

2. 组织架构(支持复杂层级)

采用 邻接表 + 物化路径 + 闭包表 混合设计,兼顾查询性能与维护成本。

-- 部门/组织表
CREATE TABLE organizations (
    id           BIGSERIAL,
    tenant_id    BIGINT NOT NULL,
    parent_id    BIGINT,
    name         VARCHAR(100) NOT NULL,
    code         VARCHAR(50)  NOT NULL,
    path         VARCHAR(500) NOT NULL,  -- 物化路径 '/1/3/8/'
    sort         INT DEFAULT 0,
    leader_id    BIGINT,                 -- 负责人 user_id
    status       SMALLINT DEFAULT 1,
    created_at   TIMESTAMP DEFAULT now(),
    updated_at   TIMESTAMP,
    PRIMARY KEY (id),
    CONSTRAINT uq_tenant_org_code UNIQUE (tenant_id, code),
    CONSTRAINT fk_org_parent FOREIGN KEY (parent_id) REFERENCES organizations(id)
);
CREATE INDEX idx_org_tenant_parent ON organizations(tenant_id, parent_id);
CREATE INDEX idx_org_path ON organizations(tenant_id, path varchar_pattern_ops); -- 支持 like 查询

-- 闭包表(高效子树查询)
CREATE TABLE organization_closures (
    ancestor_id   BIGINT NOT NULL,
    descendant_id BIGINT NOT NULL,
    depth         INT NOT NULL,          -- 层级深度 0 表示自身
    tenant_id     BIGINT NOT NULL,
    PRIMARY KEY (ancestor_id, descendant_id)
);
CREATE INDEX idx_closure_descendant ON organization_closures(tenant_id, descendant_id);
CREATE INDEX idx_closure_ancestor ON organization_closures(tenant_id, ancestor_id);

维护策略:在组织移动/新增时,通过事务更新 pathclosure 表,确保一致性。

3. 用户 - 组织关系(多组织兼职)

CREATE TABLE user_organizations (
    user_id    BIGINT NOT NULL,
    org_id     BIGINT NOT NULL,
    is_primary BOOLEAN DEFAULT false,    -- 主部门标识
    PRIMARY KEY (user_id, org_id)
);

业务约束:同一用户仅允许一个 is_primary = true

4. RBAC 权限(角色 - 资源)

为简化且扩展性强,采用 资源树(菜单/按钮/API) + 角色关联,用资源唯一标识符作为权限点。

-- 全局资源表(无 tenant_id,共享给所有租户)
CREATE TABLE resources (
    id         BIGSERIAL PRIMARY KEY,
    parent_id  BIGINT,
    code       VARCHAR(100) NOT NULL UNIQUE, -- 唯一权限标识,如 user:view, user:create
    name       VARCHAR(100) NOT NULL,
    type       VARCHAR(20)  NOT NULL CHECK (type IN ('menu','button','api')),
    path       VARCHAR(256),                 -- 菜单路由
    icon       VARCHAR(128),
    sort       INT DEFAULT 0,
    status     SMALLINT DEFAULT 1,
    created_at TIMESTAMP DEFAULT now(),
    updated_at TIMESTAMP
);
CREATE INDEX idx_res_parent ON resources(parent_id);

-- 租户角色表
CREATE TABLE roles (
    id          BIGSERIAL,
    tenant_id   BIGINT NOT NULL,
    name        VARCHAR(64) NOT NULL,
    code        VARCHAR(64) NOT NULL,
    description VARCHAR(256),
    status      SMALLINT DEFAULT 1,
    created_at  TIMESTAMP DEFAULT now(),
    updated_at  TIMESTAMP,
    PRIMARY KEY (id),
    CONSTRAINT uq_tenant_role_code UNIQUE (tenant_id, code)
);

-- 角色 - 资源关联
CREATE TABLE role_resources (
    role_id     BIGINT NOT NULL,
    resource_id BIGINT NOT NULL,
    PRIMARY KEY (role_id, resource_id)
);

-- 用户 - 角色关联
CREATE TABLE user_roles (
    user_id BIGINT NOT NULL,
    role_id BIGINT NOT NULL,
    PRIMARY KEY (user_id, role_id)
);

5. 数据范围控制(精细数据权限)

数据范围独立于功能权限,可绑定到角色,也可分配给具体用户。

-- 数据范围类型(使用枚举更规范,这里用 CHECK 简化)
CREATE TYPE scope_type AS ENUM ('ALL', 'DEPT', 'DEPT_AND_CHILD', 'CUSTOM', 'SELF');

-- 角色数据范围
CREATE TABLE role_data_scopes (
    id        BIGSERIAL PRIMARY KEY,
    role_id   BIGINT NOT NULL,
    scope     scope_type NOT NULL,
    org_ids   BIGINT[],             -- CUSTOM 时有效,其他为空
    created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_rd_scope_role ON role_data_scopes(role_id);

-- 用户数据范围(可选,用于额外授权)
CREATE TABLE user_data_scopes (
    id        BIGSERIAL PRIMARY KEY,
    user_id   BIGINT NOT NULL,
    scope     scope_type NOT NULL,
    org_ids   BIGINT[],
    created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_uds_scope_user ON user_data_scopes(user_id);

6. 审计与安全

CREATE TABLE audit_logs (
    id            BIGSERIAL,
    tenant_id     BIGINT NOT NULL,
    user_id       BIGINT,
    action        VARCHAR(50) NOT NULL,      -- 如:CREATE, UPDATE, DELETE
    resource      VARCHAR(100) NOT NULL,    -- 操作资源类型,如 'User', 'Order'
    resource_id   VARCHAR(64),
    old_value     JSONB,
    new_value     JSONB,
    ip_address    INET,
    user_agent    TEXT,
    created_at    TIMESTAMP DEFAULT now()
) PARTITION BY RANGE (created_at);        -- 大数据量时按月分区
CREATE INDEX idx_audit_tenant_time ON audit_logs(tenant_id, created_at);
  • old_value / new_value 用 JSONB 记录变更前后差异。
  • 密码使用 bcrypt 哈希存储于 users.password_hash

三、核心机制实现

1. 统一多租户隔离

  • Gin 中间件:解析 JWT → 提取 tenant_id, user_id → 存入 c.Set("tenant_id", tid)
  • GORM 全局回调
    db.Callback().Query().Before("gorm:query").Register("tenant_filter", func(db *gorm.DB) {
        if tid, ok := db.Statement.Context.Value("tenant_id").(int64); ok {
            db.Where("tenant_id = ?", tid)
        }
    })
    // 同理应用于 Create / Update / Delete
    
  • 这样业务代码无需显式携带 tenant_id,从源头杜绝数据越权。

2. 复杂组织层级查询

利用 闭包表 高效获取子树/所有祖先:

  • 查询某部门所有子部门(含自身):
    SELECT descendant_id FROM organization_closures
    WHERE tenant_id = ? AND ancestor_id = ?;
    
  • 在物化路径 path 上建立索引,直接 WHERE path LIKE '/1/3/%' 也可作为辅助手段。

3. 精细数据范围计算

合并规则(优先级:ALL > 其他):

  1. 获取用户所有角色对应的 role_data_scopes
  2. 加上 user_data_scopes(代表额外授权)。
  3. 若任一为 ALL → 数据全可见。
  4. 否则逐条解析:
    • DEPT → 使用 user_organizationsis_primaryorg_id
    • DEPT_AND_CHILD → 获取主部门及其所有子代 org_id 列表(通过闭包表)。
    • CUSTOM → 直接使用 org_ids 数组。
    • SELF → 记录一个标记,业务查询时转为 created_by = current_user_id
  5. 将所有 org_id 取并集(含 SELF 标记)。
  6. 业务 SQL 生成条件:
    -- 假设结果集包含 org_ids []int64, hasSelf bool
    WHERE (org_id IN (org_ids) OR (hasSelf AND created_by = ?))
    
    org_id IN 利用索引,高效过滤。

4. 权限校验流程

  1. 根据用户 ID 与租户,联合查询 user_roles + role_resources,得到该用户可访问的所有 resource.code 集合。
  2. 中间件拦截请求,比对当前接口的权限标识(如 user:view)是否在集合中。
  3. 通过后再进行数据范围过滤,实现功能权限 + 数据权限双重控制。

5. 扩展性功能模块

  • 动态菜单/按钮:通过 resources 表轻松增删,角色分配后即刻生效。
  • 数据范围扩展:增加新的 scope_type 值(如 AREA),并实现对应的解析逻辑。
  • 多应用支持:可在 resourcesapp_code 字段,区分不同子系统。

四、性能与数据库规范

优化点 措施
租户隔离查询 所有表 tenant_id 作为索引前缀,大量查询用覆盖索引
组织子树查询 闭包表替代递归 CTE,查询 O(1) 级延迟
权限码快速匹配 resource.code 唯一索引,集权限校验可通过缓存(Redis)存储用户权限码
审计日志 按时间分区,定期归档历史数据
数组索引 org_ids BIGINT[] 采用 GIN 索引,加速 @> 包含查询
JSONB 差异 审计日志新旧值存储,无需额外表结构

五、代码分层与 Wire 依赖注入

cmd/
  main.go                # 启动入口,wire 生成依赖
config/
  config.yaml            # 数据库、JWT等配置,由 viper 读取
internal/
  middleware/
    tenant.go            # 租户注入
    auth.go              # JWT 解析 + 权限校验
  handler/               # Gin Handler
  service/               # 业务逻辑
  repository/            # GORM 数据访问
  model/                 # 表结构模型
  wire/
    wire.go              # wire 绑定
    wire_gen.go          # 自动生成

通过 Wire 组装 DB -> Repo -> Service -> Handler,清晰可测试。


六、总结

该方案在同一个 PostgreSQL 数据库内,通过严格的 tenant_id 隔离、闭包表驱动的组织层级、角色+数据范围的双重权限控制,以及 GORM 自动注入、Gin 中间件、Wire 依赖注入,构建了一套完整、安全、高性能的多租户 RBAC 系统。所有表结构和索引均贴合业务语义,易于理解、扩展与维护。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐