# 5.9 数据权限 :::tip 概述 数据权限控制用户能看到哪些数据,由 `HasDataScope` Trait 的 `applyDataScope()` 在查询构建时按角色数据范围自动叠加过滤条件,支持三种范围:全部数据、本部门数据、仅本人数据。当前 `laravel_role` 表不含 `data_scope` 字段——`getRoleDataScope()` 读取缺省为 1,所有角色按全部数据处理。 ::: ## 三种范围 ```text ┌─────────────────────────────────────────────────────────┐ │ data_scope = 1 │ │ 全部数据(不过滤) │ │ │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ │ │ A │ │ B │ │ C │ │ D │ │ E │ ← 所有部门所有人的数据 │ │ └───┘ └───┘ └───┘ └───┘ └───┘ │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ data_scope = 2 │ │ 本部门数据 │ │ │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ │ │ A │ │ B │ │███│ │ D │ │ E │ ← 只看到本部门数据 │ │ └───┘ └───┘ └───┘ └───┘ └───┘ │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ data_scope = 3 │ │ 仅本人数据(无角色用户默认) │ │ │ │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │ │ │ A │ │ B │ │███│ │ D │ │ E │ ← 只看到自己创建的数据 │ │ └───┘ └───┘ └───┘ └───┘ └───┘ │ └─────────────────────────────────────────────────────────┘ ``` ## 配置方式 :::warning 前置条件 数据权限能力由 `HasDataScope` Trait 提供——继承 `BaseTenantService` 的服务已自动 `use` 该 Trait;直接继承 `BaseService` 的服务需自行 `use Concerns\HasDataScope`。且 **`BaseTenantService` 默认未启用数据权限**,子类必须声明字段才会生效。 ::: ```php class UserService extends BaseTenantService { /** * 数据权限:部门关联字段名(用于按部门过滤,data_scope=2 时生效) * * @var string */ protected string $dataScopeDeptField = 'dept_id'; } class ArticleService extends BaseTenantService { /** * 数据权限:归属字段名(用于按创建人过滤,data_scope=3 时生效) * * @var string */ protected string $dataScopeUserField = 'create_user'; } ``` | 属性 | 生效场景 | 过滤条件 | |------|---------|---------| | `dataScopeDeptField` | `data_scope=2` 本部门 | `WHERE {字段} = 当前用户部门ID` | | `dataScopeUserField` | `data_scope=3` 仅本人 | `WHERE {字段} = 当前用户名` | ## 实现机制 ```php // app/Services/Concerns/HasDataScope.php(行为说明) protected function applyDataScope($query) { // 未配置数据权限字段则跳过 if (empty($this->dataScopeUserField) && empty($this->dataScopeDeptField)) { return $query; } // 获取角色的数据权限范围 $dataScope = $this->getRoleDataScope($roleId); // 1=全部数据,不做过滤 if ($dataScope == 1) { return $query; } // 2=本部门数据(配置了 dataScopeDeptField 时) if ($dataScope == 2 && $this->dataScopeDeptField) { $deptId = $this->getUserDeptId($uid); if ($deptId) { $query = $query->where($this->dataScopeDeptField, $deptId); } return $query; } // 3=仅本人数据(配置了 dataScopeUserField 时) if ($dataScope == 3 && $this->dataScopeUserField) { return $query->where($this->dataScopeUserField, $username); } return $query; } ``` **查询链中的位置**(`HasQueryScopes::buildQuery`): ```text 额外固定条件($where) → LIKE 模糊匹配(pageLikeFields) → 精确匹配(pageEqFields) → 数据权限过滤(applyDataScope) ← 本节 → 租户隔离过滤(applyTenantScope) ``` :::warning 无角色用户 无角色时默认按"仅本人"(scope=3)处理——宁可少看,不可多看。 ::: ## 数据范围语义 `getRoleDataScope()` 按以下范围解析(`HasDataScope` 内部实现): | 值 | 范围 | 适用场景 | |----|------|----------| | 1 | 全部数据 | 管理层、超级管理员(当前默认) | | 2 | 本部门数据 | 部门经理 | | 3 | 仅本人数据 | 普通员工(无角色用户默认) | :::info 当前版本现状 `laravel_role` 表不包含 `data_scope` 字段(按当前表设计),角色一律解析为 1(全部数据);上表为 `HasDataScope` 的完整语义。服务只需声明 `dataScopeUserField` / `dataScopeDeptField` 字段,范围解析逻辑保持不变。 ::: ## 超级管理员 `uid=1` 的超级管理员不受数据权限限制(权限与租户校验均跳过),始终看到全部数据。详见 [5.4 权限管理(RBAC)](/zh/core/rbac.html)。 ## 与租户隔离的关系 数据权限和租户隔离是两个独立的维度: - **租户隔离**:按 `tenant_id` 过滤,不同租户数据完全隔离(`BaseTenantService` 自动启用) - **数据权限**:在同一租户内,按部门或创建人进一步过滤(需声明字段启用) 两者可以同时生效,租户隔离优先级更高(先租户、后数据权限)。详见 [5.8 多租户隔离](/zh/core/tenant.html)。 ## 常见问题 | 现象 | 原因 | 处理 | |------|------|------| | 配置了字段但过滤不生效 | 服务未启用 `HasDataScope` | 继承 `BaseTenantService` 或手动 `use` Trait | | 普通用户看到全部数据 | 当前角色数据范围解析为 1(表无 `data_scope` 字段) | 属于当前版本现状,见上文「数据范围语义」 | | 部门过滤失效(scope=2) | 未配置 `dataScopeDeptField` 或用户无部门 | 声明字段并给用户分配部门 | | 仅本人过滤失效(scope=3) | 未配置 `dataScopeUserField` | 声明字段(如 `create_user`) |