# 权限架构全面优化改造方案 > 适用范围:`leaudit-platform` 当前“角色权限 + 地区隔离”体系 > 目标:在不推翻现有 RBAC 表结构和已落地业务逻辑的前提下,把分散的权限判断、数据范围控制、菜单兼容逻辑收敛为一套可持续演进的平台化能力。 --- ## 1. 结论先行 当前项目的权限架构不是“缺权限系统”,而是已经形成了比较清晰的主干: - 认证层:`JWT + sso_users` - 功能权限层:`roles / user_role / permissions / role_permissions` - 菜单权限层:`sys_routes / role_route` - 数据隔离层:`area + role/data_scope + 业务模块自定义过滤` 从现状看,项目的真实成熟度是: - **功能权限成熟度较高** - **菜单权限已基本成型,但仍有兼容 fallback** - **数据范围权限设计完整,但平台统一执行能力不足** - **部分模块已经自行实现范围控制,但实现风格不统一** 因此,本次改造**不建议推翻现有表设计重做**,而应采取“保留模型、统一执行、逐步收敛”的路径: 1. 保留现有 `RBAC + 单地区隔离` 总体模型 2. 新增统一的“数据范围解析器 + 查询过滤构建器” 3. 将业务模块里的手写 `is_global/can_manage/area/created_by` 逻辑逐步平台化 4. 逐步取消前后端路由 fallback,最终只认数据库路由与权限点 5. 将“管理能力”从角色硬编码逐步迁移到显式权限点驱动 6. 对 legacy 模块和特殊访问模型做分层治理,而不是强行一刀切 --- ## 2. 当前代码架构全景 ## 2.1 认证与登录态 当前 JWT 只保留鉴权链路所需的最小字段,不把完整 `roles/permissions` 写进 token。 关键实现: - `fastapi_common/fastapi_common_security/jwtService.py` - `fastapi_modules/fastapi_leaudit/services/impl/authServiceImpl.py` 已确认的行为: - JWT 中保存:`user_id`、`username`、`area`、`user_role` 等最小身份信息 - 登录响应和 `/auth/me` 返回完整 `roles`、`permissions` - 这样避免了权限过多导致前端 session/cookie 体积膨胀 这是合理设计,应保留。 ## 2.2 RBAC 核心数据模型 当前权限核心表由 `scripts/创建sql/user_rbac_schema_patch.sql` 定义,结构完整,且和现有业务实现一致: - `sso_users` - `roles` - `user_role` - `sys_routes` - `role_route` - `permissions` - `role_permissions` 其中最关键的设计语义是: - 用户地区主字段:`sso_users.area` - 角色默认数据范围:`roles.data_scope` - 角色-权限点级数据范围:`role_permissions.data_scope` - 拒绝优先机制:`role_permissions.grant_type = GRANT / DENY` - `DEPT` 在本项目语义里其实是“同地区”,不是传统组织部门 这套模型本身没有明显结构性错误,问题主要出在“平台执行层不统一”。 ## 2.3 后端功能权限校验 当前后端权限判断主要分两类: 1. 通用 permission 校验 2. 某些管理域额外加角色型上下文判断 关键实现: - `fastapi_modules/fastapi_leaudit/services/impl/permissionServiceImpl.py` - `fastapi_modules/fastapi_leaudit/services/impl/rbacAdminServiceImpl.py` 现状特点: - `PermissionServiceImpl` 已支持: - 数据库动态查权 - `DENY` 优先 - wildcard 权限匹配 - 但它**只返回“有没有该权限点”**,**不解析 data_scope** 这意味着: - 平台层只完成了“功能准入” - 没完成“数据范围落地” ## 2.4 菜单与页面权限 当前菜单权限总体是数据库驱动,但仍带兼容降级逻辑。 关键实现: - 后端:`fastapi_modules/fastapi_leaudit/services/impl/rbacServiceImpl.py` - 前端:`legal-platform-frontend/lib/api/legacy/auth/user-routes.ts` - 前端侧边栏:`legal-platform-frontend/components/layout/Sidebar.tsx` 现状行为: - 后端优先从 `role_route + sys_routes` 生成当前用户路由树 - 若数据库路由集合未达到“前端真实路径集”的预期,则回退到 `_COMPAT_ROUTE_BLUEPRINTS` - 前端仍保留静态 fallback menu 和本地权限 map 兼容逻辑 这说明系统已经向“数据库驱动权限菜单”演进,但尚未完全完成切换。 ## 2.5 数据范围控制 当前数据范围控制最关键的事实是: - **设计层已经有 data_scope** - **平台层没有统一数据范围执行器** - **业务层各模块自行实现范围过滤** 已确认的代表实现: - 文档:`fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py` - 公文:`fastapi_modules/fastapi_leaudit/services/impl/govdocServiceImpl.py` - 统计:`fastapi_modules/fastapi_leaudit/services/impl/usageStatsServiceImpl.py` 这些模块大量采用类似逻辑: - `is_global` => 全量 - `can_manage + area` => 本地区 - 普通用户 => `created_by/current_user_id` 这套逻辑本身没有错,但它是“业务重复实现”,不是“权限平台统一执行”。 ## 2.6 特殊模块访问模型 项目里并非所有模块都纯粹按 `area + data_scope` 控制,存在三类特殊形态: ### 2.6.1 RAG 模块 关键实现: - `fastapi_modules/fastapi_leaudit/services/impl/ragDatasetServiceImpl.py` - `fastapi_modules/fastapi_leaudit/services/impl/ragChatServiceImpl.py` 访问特点: - 管理侧按 `UserRole + UserArea` 控制可管理知识库范围 - 使用侧按 `area in (user_area, '省级', '') or is_public` 控制可见应用和知识库 - 这是一种“地区 + 公共可见”混合模型 ### 2.6.2 交叉评查模块 关键实现: - `fastapi_modules/fastapi_leaudit/services/impl/crossReviewServiceImpl.py` 访问特点: - 主要按“任务成员关系”控制 - 用户是否能看任务、提案、投票、文档,核心依赖 `task_member` - 这不是纯 area 访问模型,而是典型 ABAC/关系型访问模型 ### 2.6.3 规则与规则配置模块 关键实现: - `fastapi_modules/fastapi_leaudit/services/impl/ruleServiceImpl.py` - `fastapi_modules/fastapi_leaudit/services/impl/ruleConfigServiceImpl.py` 访问特点: - 更偏“配置域”和“元数据域” - 当前更多依赖功能权限,不强依赖 area/data_scope - 但与评查点分组、文档类型、入口模块存在管理域耦合 --- ## 3. 当前架构的优点 ## 3.1 主模型已经稳定 项目已经从复杂化、多地区、多入口的旧思路收敛到: - 一个用户一个主地区 - 少量角色 - 菜单权限和动作权限分层 - 数据权限依赖地区和用户归属 这个方向是正确的。 ## 3.2 功能权限表设计足够支撑未来扩展 当前 `permissions + role_permissions` 已经具备: - API 权限点建模 - route 关联能力 - `GRANT / DENY` - `data_scope` - `condition_filter` 说明现有模型不是只能做静态角色,而是具备平台化扩展空间。 ## 3.3 多个业务模块已经形成真实可用的数据隔离逻辑 虽然还不统一,但文档、公文、统计等核心模块已经不是裸奔状态。 这带来两个好处: - 当前系统并非需要从零补安全 - 可直接抽象这些已验证逻辑,作为统一执行器的来源 ## 3.4 登录态瘦身思路正确 JWT 只存最小字段、详细身份通过接口返回,这一点对后续权限扩展非常重要,应保持不变。 --- ## 4. 当前关键问题与风险 ## 4.1 最大问题:`data_scope` 已建模,但没有平台级统一执行 这是整个权限架构当前最大的结构性缺口。 现象: - `roles.data_scope`、`role_permissions.data_scope` 已存在 - 但 `PermissionServiceImpl` 不返回 scope - 业务层查询时拿不到统一 scope 解析结果 - 各业务模块只能自己写过滤逻辑 后果: - 同一权限点在不同模块中可能出现不同数据边界 - 新模块开发时容易漏加范围过滤 - 审计和排查越权时需要逐模块人工追代码 - 无法形成统一测试基线 ## 4.2 第二大问题:管理能力仍部分依赖角色硬编码 典型例子: - `documentServiceImpl.py` - `rbacAdminServiceImpl.py` - `ragDatasetServiceImpl.py` 这些实现大量依赖: - `role_key in ('super_admin', 'provincial_admin')` - `role_key in ('super_admin', 'provincial_admin', 'admin')` 风险: - 后续若新增“某领域管理员”或更细角色,代码要到处改 - 角色含义和权限含义耦合 - 难以实现真正的“显式授权优先” ## 4.3 菜单权限仍存在 fallback 路径 这会带来两个风险: 1. 数据库配置与前端实际展示不完全一致 2. “菜单看得见/看不见”与“数据库已授权/未授权”之间可能出现认知偏差 虽然 fallback 是合理的迁移兼容机制,但不应长期存在于最终架构中。 ## 4.4 数据范围规则语义未彻底统一 当前系统里至少并存以下几种语义: - `ALL / DEPT / SELF` - `is_global / can_manage / is_super_admin` - `area` - `created_by` - `owner` - `public` - `task_member` 这些语义没有错,但缺少统一分类: - 哪些属于通用 scope - 哪些属于模块级关系规则 - 哪些属于资源属性规则 这会使平台边界越来越模糊。 ## 4.5 legacy 模块治理程度不一致 评查点、规则、旧库桥接模块仍带有比较重的历史包袱,典型风险包括: - 仍依赖旧库结构 - 通过参数而不是统一 user context 控制 area - 控制器层做了 permission 校验,但服务层范围收口不足 这种模块最容易成为越权和维护复杂度的来源。 ## 4.6 缺少统一的权限可观测性 当前系统基本没有形成统一的: - 权限命中日志 - data_scope 解析日志 - 被拒绝原因模型 - 权限矩阵巡检工具 结果是: - 越权排查慢 - 权限配置错误定位难 - 测试覆盖很难自动化 --- ## 5. 目标架构设计 ## 5.1 总体原则 目标架构不是“把所有访问控制都压成一个万能函数”,而是分三层: 1. **功能权限层** - 判断用户能不能调用某个动作 2. **通用数据范围层** - 判断该动作默认可以看到哪些数据 3. **模块关系规则层** - 处理 `public`、`task_member`、`owner`、`state` 这类非通用访问规则 最终形成: - RBAC 负责“能不能做” - Scope Resolver 负责“默认能看多少” - Module Policy 负责“该模块的特殊访问规则” ## 5.2 建议新增的核心组件 建议在后端新增一组统一能力: ### 5.2.1 `PermissionDecisionService` 职责: - 输入:`user_id + permission_key` - 输出:完整授权决策对象,而不是简单布尔值 建议返回: - `allowed` - `grant_source_roles` - `grant_type` - `effective_data_scope` - `condition_filter` - `is_super_admin` ### 5.2.2 `DataScopeResolver` 职责: - 解析用户当前对某权限点的最终数据范围 规则建议: 1. 汇总用户所有命中角色 2. 处理 `DENY` 优先 3. 若权限点级 `data_scope` 存在,优先使用权限点级规则 4. 若不存在,回退到角色默认 `roles.data_scope` 5. 多角色命中时,对 `GRANT` 结果按最大可见范围求并集 建议统一优先级: - `ALL > DEPT > SELF` `GROUP` 当前仅保留兼容,不建议作为第一阶段重点。 ### 5.2.3 `ScopeContextProvider` 职责: - 统一加载当前用户上下文 建议提供: - `user_id` - `area` - `roles` - `is_super_admin` - `is_global_admin` - `is_area_admin` 注意:这里的 `is_global_admin/is_area_admin` 只是兼容派生属性,不能替代显式权限本身。 ### 5.2.4 `QueryScopeBuilder` 职责: - 把 scope 结果编译成 SQLAlchemy/SQL 过滤条件 建议能力: - `apply_area_scope(table.region)` - `apply_creator_scope(table.created_by)` - `apply_owner_scope(table.owner_id)` - `apply_union_scope(...)` - `apply_public_fallback(...)` 这样业务模块不再自己拼接“同样一套 if/else”。 ### 5.2.5 `ModulePolicy` 职责: - 承接模块级特殊规则 例如: - `CrossReviewPolicy`:任务成员可见 - `RagDatasetPolicy`:地区可见 + 公开资源可见 - `RuleConfigPolicy`:配置域权限,不强制 area 这一步非常关键,因为它避免把所有特殊访问规则硬塞进通用 data_scope。 --- ## 6. 分层改造方案 ## 6.1 认证层改造 ### 保留 - JWT 只存最小身份字段 - 完整角色/权限通过 `/auth/me` 和登录响应返回 ### 优化项 1. 增加统一身份上下文对象 2. 登录/鉴权后将 `user_id/area/user_role/roles` 统一注入请求上下文 3. 补充权限版本号或 identity revision 字段,便于缓存失效 4. 为 `/auth/me` 增加可观测字段: - `roles` - `permissions` - `primary_role` - `area` ### 不建议做的事 - 不建议把全部权限点重新塞回 JWT - 不建议让前端自己根据角色推导数据范围 ## 6.2 RBAC 模型层改造 ### 保留 - 现有 7 张核心权限表 ### 优化项 1. 明确 `DEPT` 在项目中的正式定义就是“同地区” 2. 明确 `GROUP` 仅兼容保留,除非有真实业务场景再启用 3. 为 `permissions` 增加更强的治理约束: - 命名规范 - route 绑定规范 - 是否参与 data_scope 控制 4. 为 `role_permissions` 制定使用规则: - 默认只用 `GRANT` - `DENY` 只用于少数高风险覆盖场景 - `data_scope` 必须显式说明是否覆盖角色默认范围 ### 建议新增字段 如后续需要,可增量补充: - `permissions.scope_strategy` - `none` - `area` - `creator` - `owner` - `module_policy` - `permissions.resource_code` - 标记该权限点对应的数据资源模型 这不是第一阶段必须项,但非常利于后续平台化。 ## 6.3 权限决策层改造 建议把当前 `PermissionServiceImpl` 从: - `CheckPermission(UserId, PermissionKey) -> bool` 扩展为: - `GetPermissionDecision(UserId, PermissionKey) -> PermissionDecision` 旧布尔接口保留,作为新决策接口的简化包装。 建议新对象至少包括: - `allowed` - `effective_scope` - `granted_by_roles` - `denied_by_roles` - `condition_filter` 这样业务代码就不再需要: - 先查 permission - 再自己查角色 - 再自己推导 scope ## 6.4 数据范围执行层改造 这是本次改造优先级最高的部分。 ### 第一步:沉淀统一 scope 语义 统一三类通用 scope: - `ALL` - `DEPT` - `SELF` 统一基础含义: - `ALL`:不过滤地区/用户 - `DEPT`:按 `业务表地区字段 = 当前用户.area` - `SELF`:按 `created_by / owner_id / uploaded_by = 当前用户.id` ### 第二步:沉淀统一查询构建器 把文档、公文、统计中已经重复出现的模式抽象出来,避免每个模块重复写: - `if is_global` - `if can_manage` - `else current_user` ### 第三步:支持“通用 scope + 模块 policy”组合 例如: - 文档:scope 直接决定 - RAG:scope + `is_public` - 交叉评查:scope 不是主轴,改由 task-member policy 主导 ## 6.5 前端权限消费层改造 目标是让前端变成“消费后端权限结果”,而不是“自己做权限解释器”。 ### 改造目标 1. 前端菜单只消费后端 `/rbac/user/routes` 2. 前端不再长期依赖静态 fallback menu 3. `permission_map` 只作为缓存,不作为真相源 4. 页面级和按钮级权限统一从同一权限模型读取 ### 渐进策略 1. 先补齐数据库路由集合 2. 再让后端 `GetCurrentUserRoutes` 永远走数据库分支 3. 最后删除前后端 fallback 蓝图和静态菜单 ## 6.6 管理后台配置层改造 当前 RBAC 管理模块已经具备较强基础,但应继续完善为“可运营权限中心”。 建议新增或加强: 1. 角色数据范围可视化 2. 权限点是否覆盖角色默认范围的展示 3. 某角色最终可见菜单、权限点、scope 的预览 4. 某用户最终权限展开页 5. 权限冲突检测 6. 未绑定 route 的 permission 提示 7. 已配置但代码未消费的 permission 提示 --- ## 7. 重点模块改造建议 ## 7.1 文档模块 当前状态: - 数据范围实现较成熟 - 逻辑已具备抽象价值 建议: 1. 把 `_getCurrentUserContext` 提升为公共能力 2. 把 `_buildDocumentScopeFilters` 抽到统一 `QueryScopeBuilder` 3. 文档列表、详情、历史版本、删除、评查结果全部统一走同一 scope 解析逻辑 4. 把 `created_by`、`region`、交叉评查任务访问拆成: - 通用文档 scope - 跨任务关系访问补充 目标: - 文档模块作为第一批标准化样板模块 ## 7.2 公文模块 当前状态: - 与文档模块类似,也已有地区/用户过滤实现 建议: 1. 复用文档统一 scope builder 2. 不再保留模块私有版本的同构过滤逻辑 3. 把差异保留在字段映射层,而不是权限判断层 ## 7.3 统计模块 当前状态: - 已有 area/user/document 维度过滤 - 对数据口径一致性要求高 建议: 1. 明确“统计可见范围必须不大于源数据可见范围” 2. 所有统计查询先通过同一 scope resolver 得到可见边界 3. 再在统计 SQL 中应用该边界 4. 对 overview/trends/by-users/by-areas/details 建立统一基线测试 ## 7.4 RAG 模块 当前状态: - 使用了 `UserArea + UserRole + is_public` - 具备自己的可见性模型 建议: 1. 把 RAG 归类为“area + public mixed policy” 2. 通用层负责给出: - 当前用户是否全省 - 当前用户地区 - 当前 permission 的基础 scope 3. RAG 模块 policy 再补: - `is_public = TRUE` 时的跨区可见 - `'省级'`、空地区默认资源的可见规则 4. 后续将“是否可管理知识库”从角色硬编码逐步迁移到显式 permission 决策 目标: - RAG 继续保留业务特性,但不再分散复制 `UserRole` 判断 ## 7.5 交叉评查模块 当前状态: - 主要按任务成员关系控制 - 属于关系型访问控制,不应生硬纳入 `ALL/DEPT/SELF` 建议: 1. 将其定义为独立 `CrossReviewPolicy` 2. `permission_key` 只决定用户能否进入该功能域 3. 具体资源访问由: - `task_member` - `assigner` - `principal` - `proposal_owner` 等关系决定 4. 需要补充与文档主访问边界的关系定义: - 交叉评查任务中的文档是否可突破原始文档 `SELF` - 若可突破,突破边界必须仅限任务上下文 目标: - 把交叉评查明确定位为“关系型权限域”,而不是强行纳入普通地区 scope ## 7.6 评查点 / 规则 / legacy 模块 当前状态: - 部分实现仍偏 legacy - 与旧库桥接较多 - 权限收口不够彻底 建议: 1. 先做权限与数据流梳理 2. 梳理所有入口: - 列表 - 详情 - 新增 - 更新 - 删除 - 导入导出 3. 对每个接口明确: - 只需要功能权限 - 还是还要 data_scope 4. 对旧库访问统一增加 user context 入参约束 5. 禁止仅依赖外部传入 `area` 参数作为最终数据边界 --- ## 8. 管理权限治理方案 当前系统里“能否管理”仍较多通过角色推导: - `provincial_admin` - `admin` - `super_admin` 建议分两阶段治理。 ## 8.1 第一阶段 保留现有角色硬编码兼容判断,但所有新功能必须同时引入显式 permission。 即: - 角色硬编码只作为兼容 guard - 新方案以 permission 为主 ## 8.2 第二阶段 将“管理能力”全面迁移为权限点驱动: - `system:settings:manage` - `rbac:role_permissions:write` - `rag:dataset:manage` - `rules:config:manage` 最终目标: - “你是不是 admin”不再直接等于“你能不能管理 X” - 改成“你是否拥有该领域管理权限” --- ## 9. 硬编码角色改造专项分析 这部分必须单独拿出来,因为它不是“代码风格问题”,而是当前权限架构演进时最大的联动风险来源之一。 当前项目中的角色硬编码,至少存在 4 种不同形态: 1. **服务层派生上下文硬编码** 2. **服务层直接放行业务动作硬编码** 3. **前端展示/编辑能力硬编码** 4. **前端 guard / fallback / role mapping 硬编码** 如果不把这 4 类拆开治理,只改其中一层,会出现“后端收敛了,前端仍按旧角色逻辑放大/缩小能力”的不一致问题。 ## 9.1 已确认的后端硬编码角色热点 ### A. 通用上下文派生型 这些地方不是直接判断某个 permission,而是先把角色硬编码推导为: - `is_global` - `can_manage` - `is_super_admin` - `bypass_area` 代表位置: - `documentServiceImpl.py` - `govdocServiceImpl.py` - `usageStatsServiceImpl.py` - `contractTemplateServiceImpl.py` - `rbacAdminServiceImpl.py` - `homeServiceImpl.py` 典型模式: - `role_key IN ('super_admin', 'provincial_admin') => is_global` - `role_key IN ('super_admin', 'provincial_admin', 'admin') => can_manage` - `role_key = 'super_admin' => is_super_admin / bypass_area` 问题本质: - 这是把“角色名称”直接当成“能力模型” - 后续新增任意领域型管理员时,所有上下文派生代码都得重复修改 ### B. RAG 管理动作直判型 代表位置: - `ragDatasetServiceImpl.py` - `ragChatServiceImpl.py` 典型模式: - `UserRole not in ("provincial_admin", "admin", "super_admin")` - `user_role == "provincial_admin"` - `UserRole == "admin"` 问题本质: - 控制器层已经有 permission 校验 - 服务层又追加了一层角色白名单校验 - 结果是“permission 允许但角色名不在白名单”时仍会被拒绝 这会直接阻断未来“非 admin 角色但拥有 RAG 管理权限”的扩展能力。 ### C. 首页和特殊入口绕过型 代表位置: - `homeServiceImpl.py` 典型模式: - `super_admin` 直接 `bypass_area` 问题本质: - 首页入口可见性实际上也属于权限结果的一部分 - 不应长期依赖单角色绕过 ### D. 业务规则附带角色语义型 代表位置: - `contractTemplateServiceImpl.py` 典型模式: - 只有“地区管理员”可上传模板 - 但实现仍由 `can_manage/is_global/is_area_admin` 这类角色派生结果承担 问题本质: - 这是典型“业务域管理能力” - 应转换成合同模板域显式权限,而不是继续绑死在 admin/provincial_admin 上 ## 9.2 已确认的前端硬编码角色热点 ### A. RAG 知识库管理 UI 代表位置: - `components/dify-dataset-manager/index.tsx` - `components/dify-dataset-manager/area-dataset-config.tsx` - `hooks/use-area-dataset-config.ts` 典型模式: - `provincial_admin` 可编辑全部 - `super_admin` 可编辑全部 - `admin` 仅可编辑本地区 - `canManageDataset = roleCanManageDataset || permissionBased` 问题本质: - 当前前端同时混用了“角色白名单”和“权限点判断” - 即使后端后续去角色化,前端 UI 仍会保留旧角色语义 ### B. 路由 fallback 与角色映射 代表位置: - `legal-platform-frontend/lib/auth/user-routes.ts` - `legal-platform-frontend/lib/api/legacy/auth/user-routes.ts` 典型模式: - `provincial_admin -> admin` - `super_admin -> admin` - `developer -> admin` 问题本质: - 这是典型迁移兼容代码 - 如果长期保留,会把真实角色体系压扁成前端的少数桶位 ### C. 页面 guard 代表位置: - `legal-platform-frontend/lib/auth/guard.ts` 典型模式: - `developer` 或 `provincial_admin` 才能进入某些设置页 问题本质: - 这是明显的角色名授权 - 应改成 route/permission 授权,不应继续靠用户主角色字符串 ### D. 特殊入口可见性辅助逻辑 代表位置: - `legal-platform-frontend/lib/auth/cross-checking-access.ts` 典型模式: - `provincial_admin` 自动加入“省局” area 候选 问题本质: - 这类逻辑属于“前端对后端权限模型的二次解释” - 如果保留过多,会持续制造边界分叉 ## 9.3 为什么这些硬编码不能直接一刀删除 因为它们承担的职责并不相同。 ### 第一类:可立即替换 - 服务层“动作白名单式”角色判断 - 例如 RAG 管理接口中的 `UserRole not in (...)` 这类逻辑可以优先替换为: - 控制器层 permission 决策 - 服务层 scope/policy 决策 ### 第二类:需先抽象再替换 - `is_global` - `can_manage` - `is_super_admin` - `is_area_admin` 这些并不是最终目标,但当前很多模块都依赖它们构建 SQL 过滤条件。 因此正确做法不是直接删掉,而是先把它们提升为统一上下文派生结果: - `ScopeContextProvider` - `AdminCapabilityResolver` 之后再逐步把其实现从“角色硬编码”替换为“权限/能力决策”。 ### 第三类:应保留为兼容层,但必须收缩边界 - 前端 role mapping - fallback menu - 部分特殊入口兼容逻辑 这些逻辑在迁移期可以保留,但必须: 1. 标记为兼容层 2. 只允许出现在前端适配层 3. 不允许继续扩散到服务层和新功能 ## 9.4 推荐改造策略 ### 第一阶段:建立统一能力派生层 新增统一能力对象,例如: - `is_super_admin` - `has_global_scope` - `can_manage_area_resources` - `can_manage_rbac` - `can_manage_rag_dataset` - `can_view_usage_stats` 这些字段应由: - `roles` - `permissions` - `data_scope` - 可选 `condition_filter` 综合解析,而不是仅看 `role_key`。 ### 第二阶段:逐模块替换服务层角色直判 优先顺序建议: 1. `ragDatasetServiceImpl.py` 2. `rbacAdminServiceImpl.py` 3. `homeServiceImpl.py` 4. `contractTemplateServiceImpl.py` 5. 文档/公文/统计共用上下文派生 ### 第三阶段:前端去角色化 前端改造原则: 1. UI 可见性优先使用 permission 2. 可编辑性使用后端返回的 capability 或 policy 结果 3. `user_role` 只保留展示和极少数兼容用途 4. 不再让前端自行推导“谁是管理员” ## 9.5 不同硬编码的替换目标 建议按下表理解: - `super_admin` 全局绕过 替换为:`is_super_admin` capability,仅在极少数系统级接口保留 - `provincial_admin => is_global` 替换为:`effective_scope == ALL` - `admin => can_manage` 替换为:领域型 manage permission + `effective_scope == DEPT` - `common => self only` 替换为:`effective_scope == SELF` - `provincial_admin/super_admin/admin` 共同白名单 替换为:某领域 `manage/create/update/delete` permission 这一步很关键,因为它把“角色名语义”拆成了“能力语义”。 --- ## 10. 受影响接口与联动面分析 角色硬编码改造不会只影响几个 service,而会沿着“认证 -> 控制器 -> 服务层 -> 前端 UI -> 菜单/guard”整条链路传播。 因此必须提前识别影响面。 ## 10.1 高影响后端接口组 ### A. 文档模块 受影响接口包括但不限于: - `POST /api/upload` - `GET /api/documents/list` - `GET /api/documents/status` - `GET /api/documents/{DocumentId}` - `GET /api/v3/review-points/{DocumentId}` - `PATCH /api/v3/review-points/{ReviewPointResultId}/audit` - `PATCH /api/v3/documents/{DocumentId}/confirm` - `POST /api/documents/{DocumentId}/attachments` - `PUT /api/documents/{DocumentId}` - `DELETE /api/documents/{DocumentId}` 影响原因: - 这些接口都依赖文档服务里的用户上下文派生和范围过滤 - 一旦 `is_global/can_manage` 计算方式改变,所有文档读写边界都会联动变化 ### B. 公文模块 受影响接口包括但不限于: - `POST /api/govdoc/documents` - `GET /api/govdoc/documents` - `GET /api/govdoc/documents/{documentId}` - `PATCH /api/govdoc/documents/{documentId}` - `DELETE /api/govdoc/documents/{documentId}` - `POST /api/govdoc/runs` - `GET /api/govdoc/runs/{runId}` - `GET /api/govdoc/runs/{runId}/result` - `GET /api/govdoc/runs/{runId}/findings` - `GET /api/govdoc/runs/{runId}/entities` - `GET /api/govdoc/runs/{runId}/structure` - `GET /api/govdoc/runs/{runId}/outline` - `GET /api/govdoc/runs/{runId}/paragraphs` - `GET /api/govdoc/runs/{runId}/report/html` - `GET /api/govdoc/runs/{runId}/report/docx` - `GET /api/govdoc/documents/{documentId}/original` 影响原因: - 公文模块沿用了与文档模块相似的范围控制模型 - run/result/report 等接口如果没有统一回挂到 document scope,容易出现“知道 runId 就能读结果”的风险 ### C. 统计模块 受影响接口包括: - `GET /api/v3/usage-stats/overview` - `GET /api/v3/usage-stats/trends` - `GET /api/v3/usage-stats/by-users` - `GET /api/v3/usage-stats/by-departments` - `GET /api/v3/usage-stats/by-areas` - `GET /api/v3/usage-stats/details` 影响原因: - 当前统计服务对“只有管理员可看”存在上下文派生逻辑 - 如果未来改成 permission 决策,统计域会是最先受影响的读接口集合之一 ### D. RAG 模块 受影响接口包括: - `GET /api/v3/rag/apps` - `GET /api/v3/rag/apps/default` - `GET /api/v3/rag/datasets/my` - `GET /api/v3/rag/datasets/admin` - `POST /api/v3/rag/datasets/admin` - `PUT /api/v3/rag/datasets/admin/{DatasetId}` - `DELETE /api/v3/rag/datasets/admin/{DatasetId}` - `GET /api/v3/rag/datasets/{DatasetId}` - `PATCH /api/v3/rag/datasets/{DatasetId}` - 以及数据集文档、分段、检索测试等一系列 `/datasets/{DatasetId}/...` 接口 - `POST /api/v3/rag/chat/messages` - 会话相关接口 影响原因: - 控制器层已经按 permission 放行 - 服务层仍按 `UserRole` 二次限流 - 这是最典型的“双轨权限模型冲突”区域 ### E. RBAC 管理模块 受影响接口包括: - `GET /api/v3/rbac/roles` - `POST /api/v3/rbac/roles` - `PUT /api/v3/rbac/roles/{RoleId}` - `DELETE /api/v3/rbac/roles/{RoleId}` - `GET /api/v3/rbac/users` - `GET /api/admin/users/organizations/tree` - `GET /api/v3/rbac/roles/{RoleId}/users` - `POST /api/v3/rbac/users/{UserId}/roles` - `DELETE /api/v3/rbac/users/{UserId}/roles/{RoleId}` - `GET /api/v3/rbac/users/{UserId}/roles` - `GET /api/v3/routes` - `GET/PUT /api/rbac/roles/{RoleId}/routes` - `GET/POST /api/v3/rbac/role-permissions` - `POST /api/v3/rbac/roles/{RoleId}/access` - `GET /api/v3/routes/{RouteId}/permissions` 影响原因: - 当前先 `_assertManagePermission`,再 `_assertPermission` - 其中 `_assertManagePermission` 仍依赖角色派生 `can_manage` - 未来若不调整,会出现“拥有 RBAC 写权限但不是 admin 角色”仍被拒绝的问题 ### F. 首页入口模块 受影响接口: - `GET /api/home/entry-modules` 影响原因: - 首页可见入口当前含 `super_admin` 的 area bypass 语义 - 改动后会影响首页模块展示和交叉评查入口暴露范围 ### G. 合同模板模块 受影响接口包括: - `GET /api/v3/contract-templates/categories` - `GET /api/v3/contract-templates` - `POST /api/v3/contract-templates` - `GET /api/v3/contract-templates/search` - `GET /api/v3/contract-templates/{TemplateId}` - `DELETE /api/v3/contract-templates/{TemplateId}` 影响原因: - 业务文案和服务层逻辑都在强调“地区管理员才能上传” - 这类业务域权限必须显式建模,否则角色去硬编码后会发生语义漂移 ## 10.2 中影响后端接口组 ### A. 交叉评查控制器 控制器层目前主要通过 permission 做功能准入,再由服务层按 task-member 关系控权。 受影响点: - 若未来新增“交叉评查协调员”等角色 - 控制器层 permission、服务层关系权限、前端入口策略三者都要同步 ### B. 评查点 / 评查点分组 / 规则配置 这些模块当前更多是 permission 驱动,但仍存在: - OR 型 permission 校验 - 路由 fallback - 某些 legacy 行为仍按旧角色认知理解的隐患 这类模块短期不一定要先改,但必须纳入联调验证范围。 ## 10.3 前端受影响面 ### A. 菜单与路由 受影响位置: - `Sidebar.tsx` - `user-routes.ts` - `check-route-permission.ts` - route fallback mapping 改造后可能出现的联动: - 某些角色映射桶失效 - fallback 菜单与真实 route-permission 不一致 - 页面可见但接口被拒绝,或页面不可见但后端已授权 ### B. 知识库管理相关页面 受影响位置: - `components/dify-dataset-manager/*` - `hooks/use-area-dataset-config.ts` 改造后必须改为: - 以后端返回的 permission/capability 作为真相源 - 不能继续由前端自己判定“你是不是省级管理员” ### C. 页面 guard 和 session typing 受影响位置: - `lib/auth/guard.ts` - `lib/auth/session-user.ts` - `lib/auth/jwt.ts` 改造重点: - `user_role` 类型定义不能继续隐式代表完整能力模型 - guard 逻辑要逐步转成 permission 驱动 ## 10.4 最容易遗漏的影响点 有 4 类点最容易在改造时被漏掉: 1. **服务层动作二次校验** 典型如 RAG:控制器已查 permission,但服务层又按角色拒绝 2. **只读接口的资源详情/下载接口** 典型如公文的 report/docx/original 下载 3. **首页和入口模块可见性** 这类功能通常不在权限改造 checklist 里,但实际也是访问控制 4. **前端本地缓存的 permission_map / user_role** 后端改完,前端缓存解释逻辑若不改,会出现脏权限体验 ## 10.5 推荐联动改造顺序 为了降低波及风险,建议按下面顺序推进: 1. 先改后端统一决策层,不动前端行为 2. 接入 RAG 服务层,消除“permission 通过但角色名拒绝”的双轨冲突 3. 接入文档/公文/统计通用 scope 4. 接入 RBAC 管理域 `_assertManagePermission` 5. 接入首页入口与合同模板这类特殊管理域 6. 最后清理前端角色映射、UI 角色判断和 guard 这样可以避免一开始就同时触碰: - 数据边界 - 功能权限 - 菜单展示 - 页面 guard 导致联调失控。 ## 10.6 额外建议:建立“角色去硬编码回归清单” 建议在实际实施时,单独维护一份回归清单,至少覆盖: - 某用户有 permission 但主角色不是 `admin/provincial_admin` 时,是否仍可正确访问 - 某用户被赋予新领域管理角色后,是否无需改代码即可生效 - 某用户菜单、按钮、接口、详情、下载、导出是否边界一致 - 前端是否仍存在基于 `user_role` 的旧判断把能力放大或缩小 --- ## 11. 数据库迁移方案 ## 9.1 第一阶段可不改表,仅改执行层 这是最推荐的路径。 先做: - 新增决策服务 - 新增 scope resolver - 新增 query builder - 业务模块接入 在这一步,现有表足够使用。 ## 9.2 第二阶段再做增强字段 若后续希望更强平台能力,再增量考虑: - `permissions.scope_strategy` - `permissions.resource_code` - `permissions.policy_code` - `role_permissions.scope_override_reason` 这些字段用于治理和解释,不是首批必要项。 ## 9.3 数据迁移原则 1. 不删除旧字段 2. 不重命名核心字段 3. 先兼容运行,再切换调用方 4. 所有新增字段默认 nullable,避免一次性大面积回填风险 --- ## 12. 渐进式实施路径 ## 10.1 Phase 1:统一决策能力 目标: - 先把平台底座搭好 交付: - `PermissionDecisionService` - `DataScopeResolver` - `ScopeContextProvider` - `QueryScopeBuilder` 收益: - 新老模块都可开始接入 ## 10.2 Phase 2:样板模块接入 建议顺序: 1. 文档 2. 公文 3. 统计 原因: - 这三类模块已经有成熟范围逻辑 - 抽象收益最高 - 改造风险相对可控 ## 10.3 Phase 3:菜单权限彻底数据库化 目标: - 删除路由 fallback - 删除静态菜单兼容 前提: - 数据库路由集合补齐 - 权限点-route 映射完整 ## 10.4 Phase 4:特殊模块治理 建议顺序: 1. RAG 2. 交叉评查 3. 评查点/规则/legacy 桥接模块 目标: - 形成“通用 scope + 模块 policy”模式 ## 10.5 Phase 5:治理与可观测性 补齐: - 权限命中日志 - 越权拒绝原因 - 权限矩阵导出 - 自动化巡检任务 --- ## 13. 测试与验收方案 ## 11.1 必做测试矩阵 每个核心权限点至少覆盖以下用户组合: - `super_admin` - `provincial_admin` - `admin` - `common` - 多角色用户 - 无 area 用户 - 被禁用用户 每个资源至少覆盖以下数据组合: - 同地区数据 - 异地区数据 - 自己创建数据 - 他人创建数据 - 公共数据 - 关系型共享数据 ## 11.2 必做接口验收 至少覆盖: - 登录与 `/auth/me` - 菜单获取 - 文档列表/详情/历史/删除 - 公文列表/详情 - 统计总览/趋势/明细 - RAG 应用/知识库 - 交叉评查任务/提案/投票 - RBAC 管理台 ## 11.3 必做回归断言 1. 有 permission 但无 scope 不应越权看全量 2. 菜单可见不等于接口可调用,接口仍需后端鉴权 3. 仅知道资源 ID 不应绕过数据范围校验 4. 跨模块聚合查询不能扩大原始资源可见范围 5. `DENY` 必须稳定优先于 `GRANT` ## 11.4 建议增加自动化 建议建立: - permission fixture - user-role fixture - region fixture - resource ownership fixture 让权限测试不再依赖手工造数。 --- ## 14. 风险与回滚方案 ## 12.1 主要风险 1. 抽象过度,导致特殊模块被错误套入通用逻辑 2. 改造过程中菜单和接口权限短期不一致 3. scope 规则切换后产生历史行为变化 4. 多角色并集规则理解不一致,导致边界扩大或缩小 ## 12.2 风险控制 1. 先在样板模块试点,不直接全量切 2. 新旧逻辑并行一段时间,输出对比日志 3. 为关键接口加“旧逻辑 vs 新逻辑” shadow compare 4. 管理台提供用户最终权限预览 ## 12.3 回滚策略 1. 决策服务接入采用开关控制 2. 模块级按 feature flag 切换 3. 菜单 fallback 在数据库路由完全稳定前不删除 4. 新增字段不影响旧逻辑运行 --- ## 15. 推荐实施排期 ## 第 1 周 - 完成权限现状盘点和权限点清单冻结 - 设计 `PermissionDecisionService` 和 `DataScopeResolver` - 输出统一 scope 规则说明 ## 第 2 周 - 落地统一决策与查询构建器 - 接入文档模块 - 建立第一批自动化权限测试 ## 第 3 周 - 接入公文与统计模块 - 补齐管理台权限预览能力 - 开始新旧逻辑对比日志 ## 第 4 周 - 接入 RAG - 梳理交叉评查 policy - 补齐数据库路由集合 ## 第 5 周 - 切换菜单只走数据库路由 - 处理 legacy 规则/评查点模块 - 清理前端 fallback ## 第 6 周 - 全量回归 - 安全测试 - 观察期与灰度发布 --- ## 16. 最终推荐方案 如果只给出一条最重要的改造主线,我的建议是: **不要推翻现有 RBAC 结构,优先补齐统一的数据范围执行平台。** 更具体地说,正确的改造顺序应该是: 1. 先保留现有 `sso_users/roles/permissions/routes` 模型 2. 把 `data_scope` 从“设计字段”变成“统一可执行能力” 3. 把文档、公文、统计里的重复数据范围逻辑抽成平台底座 4. 把 RAG、交叉评查这类特殊模块归入独立 policy,而不是粗暴并表 5. 最后再清理菜单 fallback 和角色硬编码 这样做的收益最大: - 改造风险最低 - 与现有代码最兼容 - 能最快提升一致性和安全性 - 能为后续模块扩展提供稳定底座 --- ## 17. 建议立项清单 建议把后续工作拆成 8 个明确任务: 1. 建立权限决策对象与统一接口 2. 实现 data_scope 解析器 3. 实现通用查询 scope builder 4. 文档模块接入统一 scope 5. 公文与统计模块接入统一 scope 6. RAG / 交叉评查 policy 化 7. 数据库路由补齐并移除 fallback 8. 建立权限可观测性与自动化测试矩阵 这 8 项完成后,当前项目的角色权限架构就会从“可用但分散”,升级为“稳定、统一、可治理、可演进”的平台化体系。