节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
7.13 KiB
Stardust
# 项目隔离与权限改进方案(最小改动版) > 制定日期:2026-07-24 > 依据:`Doc/架构审查与权限分析.md` 的权限专题结论 > 目标:实现项目级数据隔离(堵住跨项目泄露),同时满足约束——**最小改动、最简改法、复用优先、高内聚低耦合、非必要不加 ProjectId 列**。 --- ## 0. 设计约束(用户明确要求) 1. **非必要不在表中加 `ProjectId` 列**:避免 49 张表的 schema 迁移与数据回填风险。 2. **所有必须跟项目挂钩的 CRUD 都要增加项目过滤**:读(列表/详情)+ 写(新增/修改/删除)一律校验项目作用域。 3. **最小改动**:不重写每个查询;逻辑集中、各接入点单行改动。 4. **高内聚低耦合**:过滤逻辑单一职责、可单测、与业务控制器解耦。 --- ## 1. 核心思路:聚合根间接隔离 - 聚合根 `App` / `Node` **已带 `ProjectId`**(数据已存在,无需加列)。 - 绝大多数明细/时序表通过 `AppId` 或 `NodeId` 关联到聚合根 → 用 `父级.ProjectId` 间接过滤,**零新增列**。 - 过滤逻辑集中到一个可复用组件;各控制器只改基类继承(一行)或调一个扩展方法。 ``` 明细表(AppId/NodeId) → App/Node(ProjectId) → 用户允许的项目集合 ``` --- ## 2. 架构设计(三个新增、职责单一) ### 2.1 `ProjectScopeService`(解析"当前用户能看哪些项目") - 输入:当前登录用户(Cube `ManageProvider.User`)。 - 输出:`IList<Int32> AllowedProjectIds` + `Int32? SelectedProjectId`。 - 规则: - 超级管理员(特定角色)→ 允许全部项目(哨兵值)。 - 普通用户 → 来自 `ProjectUser`(用户-项目绑定表,**当前从未被查询使用,本次正式启用**)。 - `SelectedProjectId` 取自 URL `projectId`;**必须落在 Allowed 内**,否则忽略(防越权:低权用户手填他人项目 ID)。 - 每请求解析一次,缓存到 `HttpContext.Items`,避免重复查库。 ### 2.2 `ProjectFilter`(纯函数/扩展,可单测,与框架无关) - `BuildOql<TEntity>(ProjectScope scope)` 返回 XCode `OQL` / `WhereExpression`: - T 直接有 `ProjectId` → `ProjectId In(allowed)`;若 selected 非空则 `ProjectId == selected`。 - T 有 `AppId` → `AppId In(allowedAppIds)`,其中 `allowedAppIds = App.FindByProject(allowed).IDs`(按请求缓存)。 - T 有 `NodeId` → `NodeId In(allowedNodeIds)`,同理。 - T 两者皆无 → 返回 `null`(全局表,不强制;debug 下断言提醒,待人工确认是否真全局)。 - 写操作复用同一解析:`ResolveProjectId(TEntity e)` 通过父级 App/Node 反查 ProjectId,供校验。 ### 2.3 `ProjectScopedController<TEntity>`(基类,最小接入) - 继承 Cube `EntityController<TEntity>`,**集中重写**读/写钩子: - `Search(Pager p)`:取 Cube 原生 where → `where &= ProjectFilter.BuildOql<T>(scope)` → 正常分页。 - `Add/Update/Delete` 前置校验:调 `ProjectFilter.ResolveProjectId(entity)` 断言属于 Allowed;否则拒绝(防越权写)。 - 接入方式:作用域内的控制器把 `: EntityController<X>` 改为 `: ProjectScopedController<X>`(**每文件一行**)。 - 非作用域(真正全局)的控制器不动。 > 重写点确认:实施前用 DeepWiki 查 `NewLifeX/X`(NewLife.Cube)的 `EntityController.Search` 确切签名,确保重写兼容;若 Cube 不直接暴露 where,则改为在 `FindAll` 外包一层 `Search` 扩展方法(同样是集中逻辑 + 单行调用)。 --- ## 3. 实体分类与过滤方式(零加列) | 类别 | 代表表 | 过滤方式 | |------|--------|----------| | 直接 ProjectId(已存在) | App、Node、AppDeploy、AppConfig、AppTracer、AlarmGroup、GatewayCluster/Route、MySqlNode、RedisNode | 直接 `ProjectId In(allowed)` | | 经 AppId | AppService、AppConsume、AppMeter、AppHistory、AppCommand、AppClientLog;TraceData、SampleData、Trace*/App*Stat(appId);AppDeployNode/Version/History(经 AppDeploy→App) | `AppId In(allowedAppIds)` | | 经 NodeId | NodeData、NodeCommand、NodeHistory、NodeStat、NodeVersion、NodeRule、NodeLocation;RedisData/MySqlData(经 RedisNode/MySqlNode) | `NodeId In(allowedNodeIds)` | | 全局(待评审确认) | Attachment、SslCertificate、McpAudit、RedisMessageQueue 等 | 默认不强制;逐表确认父链,能挂父级则按父级过滤,确属系统级再标注全局并评审 | > 实施时逐表确认 `AppId/NodeId` 父链(数据驱动),不臆测;对拿不准的表**保守处理**(挂父链过滤或显式标注+评审),避免漏过滤。 --- ## 4. 实施步骤(最小改动顺序) 1. **新增 `ProjectScopeService`**:解析 Allowed/Selected,启用 `ProjectUser`,定义超级管理员判定,请求级缓存。 2. **新增 `ProjectFilter`**:`BuildOql<T>` + `ResolveProjectId`,纯函数、单测覆盖。 3. **新增 `ProjectScopedController<T>`** 基类:重写 Search/Add/Update/Delete 注入作用域(先确认 Cube 重写点)。 4. **接入控制器**:把作用域内的约 30 个控制器改为继承基类(每处一行)。全局表控制器不动。 5. **Server 侧 API 审计(次要)**:`Stardust.Server` 中接受 `appId/nodeId` 的接口,校验调用方身份所属项目,防止跨项目读取(客户端/代理本就凭 App/Node 凭证调用,风险低,按需补)。 6. **Vue 前台(可选)**:服务端已按用户强制作用域,Vue 无需改过滤逻辑;仅可选加"当前项目"选择器提升体验,列表请求带 `projectId` 仅作便捷。 --- ## 5. 验证 - **单测(关键)**:构造仅属 Project A 的普通用户,调用 `AppService` / `TraceData` / `ConfigData` / `AppDeployNode` 等列表 API,断言**仅含 A 数据**;手填他人 `projectId` 被拒绝。 - **回归**:超级管理员仍可见全部;分页/排序/统计不受影响。 - 复用/新增 `Stardust.ServerTests` 或 Web 集成测试覆盖上述场景。 --- ## 6. 风险与权衡 - **性能**:`AppId/NodeId In(子查询)` 作用于小且索引良好的 App/Node 表,列表分页影响可忽略;必要时把 `allowedAppIds/NodeIds` 物化到请求级缓存。 - **零迁移**:不加列 → 无 DB 迁移、无历史数据回填,部署风险最低(符合"非必要不加列")。 - **全局表误判**:靠逐表确认 + 保守默认规避;评审清单留痕。 - **耦合度**:过滤逻辑全部在 `ProjectFilter`,控制器零业务侵入;`ProjectScopeService` 仅依赖 `ProjectUser` 与 Cube 用户上下文,可独立单测。 --- ## 7. 结论 以**「聚合根间接隔离 + 集中作用域基类」**实现项目级数据隔离: - 零新增列、零 DB 迁移; - 控制器单行接入,逻辑全复用、与业务解耦; - 读/写全链路项目校验,堵住跨项目泄露; - 严格契合用户"最小改动、最简改法、高内聚低耦合、非必要不加 ProjectId"的约束。 > 本方案为计划文档,未改动任何源码,也未提交 git;待确认后进入实施。