Skip to content

5.9 数据权限 ​

概述

角色数据权限(role.data_scope)是叠加在租户隔离之上的第二层行级过滤,四档可配置。模块通过 DataScope: ApplyDataScope("dept_id") 一行声明接线,仅作用于 Page/List/导出。

四档数据范围 ​

档位含义过滤条件(作用于挂接模块的列表行)
1全部不过滤(租户隔离之内全量)
2本部门及以下dept_id IN (自身部门 + 全部子孙部门 ID)
3本部门dept_id = 自身部门 ID
4仅本人id = 自身用户 ID

档位文案走字典 role_data_scope(定稿种子已写入 dict/dict_item),角色表单"数据范围"下拉由字典驱动。

模块接线(一行声明) ​

go
// internal/logic/user.go(真实接线方式)
userSpec = ModuleSpec[...]{ ...,
    DataScope: ApplyDataScope("dept_id"),
}

ApplyDataScope(deptColumn) 返回 DataScopeFunc 闭包(logic/data_scope.go,共享工厂),挂进 applyFilters 末环:

text
Like → Eq → Keyword → Filter → DataScope
租户/软删隔离由 dao.Ctx 先于本链注入

接线后 Page/List/导出自动生效。目前仅 user 模块接线(全库仅 goframe_user 有 dept_id 列);其他模块需要时声明同款闭包即可,无部门归属列的表可按需扩展"按创建人过滤"变体。

与租户隔离的叠加关系 ​

text
第一层(硬规则,不可配置):租户隔离
  dao.Ctx 注入 tenant_id IN(0,自身),先于一切业务过滤,任何模块不可绕过

第二层(可配置):角色数据范围
  ModuleSpec.DataScope 挂点,applyFilters 末环叠加
  两层独立:先租户收窄,再数据范围收窄

安全语义(与直觉不同的点) ​

五条语义

  1. 多角色取最宽松(并集直觉)——合并发生在 LoadIdentity 缓存层(数值 min,1 最宽),热路径零额外查询
  2. 超管/平台完全绕过(IsPlatform 或 Roles 含 ROLE_SUPER_ADMIN,dataScopeBypass 双判断)
  3. 未分配部门(dept_id=0)用户挂档 2/3 时降级为"仅本人"(防 dept_id=0 意外放大可见面)
  4. 仅作用于 Page/List/导出(applyFilters 链);Detail/Update/Delete 不过滤,由 permission 节点与租户隔离兜底——管理端点授予权限时应评估该角色是否应有写权限
  5. 部门子树 deptSubtreeIds:拉全租户部门行(id,parent_id)后 BFS(树小直查无缓存;成热点可加短时缓存)

配置链路(修改如何生效) ​

text
role 表 data_scope 列(默认 1)
  → 角色表单"数据范围"下拉(字典 role_data_scope 驱动,值 1/2/3/4)
    → roleToData 归一(缺省回 1,gvalid in:1,2,3,4 校验)
      → 角色 Update 后 AfterUpdate 清该角色全部用户身份缓存
        → 生效(新请求的 LoadIdentity 读到新档位)

Page/Detail 返回 dataScope + dataScopeText(字典驱动),前端可展示当前角色数据范围。

上下文读取 ​

text
GetDataScope(ctx)   档位(1-全部;未注入视为全部——旧缓存缺字段回退安全值)
GetDeptID(ctx)      当前用户部门 ID(0=未分配)

两者由 JWT 中间件从身份缓存注入,业务代码一般不直接读(基座 DataScope 闭包内部消费)。

已知边界 ​

  • 数据权限当前仅 user 模块接线;其他模块行级可见性由租户隔离单层兜底
  • Detail/Update/Delete 不做行级过滤——越权改删他人记录的防线是接口 permission + 租户隔离
  • 身份缓存 300s 窗口内直接改库的 dept_id/data_scope 不即时生效(API 路径均主动清缓存)
  • 第 5 档自定义范围(如按部门多选)为预留能力,需部门关联表 + 角色表单扩展

小蚂蚁云团队 · 提供技术支持