1048 lines
18 KiB
Markdown
1048 lines
18 KiB
Markdown
# 老系统 `docauditai` 用户权限架构深度分析
|
||
|
||
本文档用于系统性梳理老项目:
|
||
|
||
- `/home/wren-dev/Porject/docauditai`
|
||
|
||
中的用户、认证、角色、路由、权限、地区、数据范围逻辑。
|
||
|
||
目标不是简单列表,而是明确回答:
|
||
|
||
- 老系统真实的用户权限架构是什么
|
||
- 它依赖哪些表和哪些业务规则
|
||
- 新系统应该继承什么、舍弃什么、升级什么
|
||
|
||
---
|
||
|
||
## 1. 结论先行
|
||
|
||
老系统并不是一个“只有 6 张 RBAC 表”的简单权限系统。
|
||
|
||
它真实的架构是:
|
||
|
||
- 统一登录入口:OAuth + 账密登录
|
||
- 用户主表:`sso_users`
|
||
- 角色体系:`roles + user_role`
|
||
- 菜单路由:`sys_routes + role_route`
|
||
- 权限点:`permissions + role_permissions`
|
||
- 数据权限:`role_permissions.data_scope`
|
||
- 地区隔离:大量业务依赖 `sso_users.area`
|
||
- 组织同步:登录时通过 MQ / 组织表同步 `area / tenant / dep`
|
||
|
||
也就是说,老系统本质上是:
|
||
|
||
- `RBAC + area-based data scope`
|
||
|
||
而不是纯粹的:
|
||
|
||
- `用户 - 角色 - 菜单`
|
||
|
||
---
|
||
|
||
## 2. 核心表与模块
|
||
|
||
从代码实际使用看,老系统核心不只是以下 6 张表:
|
||
|
||
- `sys_routes`
|
||
- `sso_users`
|
||
- `roles`
|
||
- `role_route`
|
||
- `role_permissions`
|
||
- `user_role`
|
||
|
||
还必须加上:
|
||
|
||
- `permissions`
|
||
- `route_permission`
|
||
- 业务表中的 `area` 字段
|
||
- `roles.data_scope`
|
||
- `role_permissions.data_scope`
|
||
|
||
相关代码位置:
|
||
|
||
- 认证:`app/routes/auth.py`
|
||
- JWT:`app/auth/auth.py`
|
||
- 路由权限:`app/rbac/route_permission.py`
|
||
- 权限检查:`app/rbac/permission_checker_v2.py`
|
||
- 数据范围:`app/rbac/data_scope_injector_v2.py`
|
||
- RBAC API:`app/routes/v3/rbac.py`
|
||
|
||
---
|
||
|
||
## 3. 登录架构
|
||
|
||
## 3.1 统一登录入口
|
||
|
||
老系统登录主入口是:
|
||
|
||
- `POST /auth/login`
|
||
|
||
代码:
|
||
|
||
- `app/routes/auth.py`
|
||
|
||
统一登录入口自动识别两种模式:
|
||
|
||
- OAuth 登录
|
||
- 账号密码登录
|
||
|
||
识别规则:
|
||
|
||
- 请求体包含 `userInfo.sub` → OAuth 登录
|
||
- 请求体包含 `username + password` → 账密登录
|
||
|
||
对应代码:
|
||
|
||
- `app/routes/auth.py:111`
|
||
- `app/routes/auth.py:154`
|
||
- `app/routes/auth.py:158`
|
||
|
||
这说明老系统在登录入口设计上已经做到了统一。
|
||
|
||
---
|
||
|
||
## 3.2 OAuth 登录逻辑
|
||
|
||
OAuth 登录处理函数:
|
||
|
||
- `app/routes/auth.py:_handle_oauth_login`
|
||
|
||
主要流程:
|
||
|
||
1. 读取前端传入的 `userInfo`
|
||
2. 通过 `sub` 查询 `sso_users`
|
||
3. 如果用户不存在,则自动创建用户
|
||
4. 如果用户已存在,则更新用户基本资料
|
||
5. 查询用户角色
|
||
6. 生成 JWT
|
||
7. 返回统一登录响应
|
||
|
||
对应代码位置:
|
||
|
||
- `app/routes/auth.py:412`
|
||
- `app/routes/auth.py:451`
|
||
- `app/routes/auth.py:518`
|
||
- `app/routes/auth.py:563`
|
||
- `app/routes/auth.py:601`
|
||
- `app/routes/auth.py:611`
|
||
|
||
### OAuth 用户创建规则
|
||
|
||
当用户不存在时:
|
||
|
||
- 自动插入 `sso_users`
|
||
- 自动分配默认角色 `common`
|
||
|
||
对应代码:
|
||
|
||
- `app/routes/auth.py:471`
|
||
- `app/routes/auth.py:512`
|
||
|
||
这意味着:
|
||
|
||
- 老系统支持 OAuth 首登自动建号
|
||
- 并且会自动授予最低默认权限
|
||
|
||
---
|
||
|
||
## 3.3 账号密码登录逻辑
|
||
|
||
密码登录处理函数:
|
||
|
||
- `app/routes/auth.py:_handle_password_login`
|
||
|
||
主要流程:
|
||
|
||
1. 根据 `username/sub` 查询 `sso_users`
|
||
2. 校验密码
|
||
3. 校验状态 / 删除标记
|
||
4. 同步组织信息(如果有)
|
||
5. 查询角色
|
||
6. 生成 JWT
|
||
7. 返回统一响应
|
||
|
||
对应代码:
|
||
|
||
- `app/routes/auth.py:655`
|
||
- `app/routes/auth.py:689`
|
||
- `app/routes/auth.py:723`
|
||
- `app/routes/auth.py:745`
|
||
- `app/routes/auth.py:756`
|
||
- `app/routes/auth.py:798`
|
||
|
||
可以看出,老系统并没有把账密登录和 OAuth 登录做成两套完全不同的人群模型。
|
||
|
||
它们最终都归并到:
|
||
|
||
- `sso_users`
|
||
- `user_role`
|
||
- `roles`
|
||
- JWT
|
||
|
||
这是一个非常重要的架构特征。
|
||
|
||
---
|
||
|
||
## 4. JWT 模型
|
||
|
||
JWT 逻辑在:
|
||
|
||
- `app/auth/auth.py`
|
||
|
||
JWT Payload 中实际携带的关键字段有:
|
||
|
||
- `user_id`
|
||
- `username`
|
||
- `user_role`
|
||
- `permissions`(可选)
|
||
- `sub`
|
||
- `nick_name`
|
||
- `email`
|
||
- `phone_number`
|
||
- `ou_id`
|
||
- `ou_name`
|
||
- `is_leader`
|
||
- `area`
|
||
|
||
对应代码:
|
||
|
||
- `app/auth/auth.py:61`
|
||
- `app/auth/auth.py:94`
|
||
- `app/auth/auth.py:101`
|
||
- `app/auth/auth.py:106`
|
||
|
||
JWT 解码后,同样会恢复成:
|
||
|
||
- `User`
|
||
- `TokenData`
|
||
|
||
对应:
|
||
|
||
- `app/auth/auth.py:24`
|
||
- `app/auth/auth.py:46`
|
||
- `app/auth/auth.py:119`
|
||
- `app/auth/auth.py:171`
|
||
|
||
### 结论
|
||
|
||
老系统 JWT 不只是认证 token,它还承担了:
|
||
|
||
- 用户身份
|
||
- 用户角色
|
||
- 用户地区
|
||
- 组织基础信息
|
||
|
||
其中 `area` 是最关键的业务字段之一。
|
||
|
||
---
|
||
|
||
## 5. 用户地区到底怎么来的
|
||
|
||
这是老系统最关键的逻辑之一。
|
||
|
||
### 5.1 不是靠前端端口
|
||
|
||
老系统的地区不是通过前端访问端口推断。
|
||
|
||
它主要通过组织主数据反查得到。
|
||
|
||
### 5.2 OAuth 登录时的地区来源
|
||
|
||
OAuth 登录会调用:
|
||
|
||
- `_get_user_org_info_from_mq`
|
||
|
||
位置:
|
||
|
||
- `app/routes/auth.py:280`
|
||
|
||
查询流程:
|
||
|
||
1. 用 `ou_id + nickname` 查询 `um_personinfo`
|
||
2. 获取:
|
||
- `tenant_uuid`
|
||
- `dep_uuid`
|
||
- `org_uuid`
|
||
3. 再查:
|
||
- `um_tenant`
|
||
- `um_department`
|
||
4. 最终组装:
|
||
- `area`
|
||
- `tenant_name`
|
||
- `dep_name`
|
||
- `dep_short_name`
|
||
- `ou_name`
|
||
|
||
其中 `area` 的来源是:
|
||
|
||
- `tenant_short_name`
|
||
- 或 `tenant_name`
|
||
|
||
对应代码:
|
||
|
||
- `app/routes/auth.py:206`
|
||
- `app/routes/auth.py:263`
|
||
- `app/routes/auth.py:280`
|
||
- `app/routes/auth.py:380`
|
||
- `app/routes/auth.py:435`
|
||
|
||
### 5.3 用户首次创建时
|
||
|
||
新用户创建时,组织信息会直接写入 `sso_users`:
|
||
|
||
- `area`
|
||
- `tenant_name`
|
||
- `dep_name`
|
||
- `dep_short_name`
|
||
- `ou_name`
|
||
|
||
对应:
|
||
|
||
- `app/routes/auth.py:471`
|
||
- `app/routes/auth.py:480`
|
||
- `app/routes/auth.py:512`
|
||
|
||
### 5.4 老用户每次登录时
|
||
|
||
如果用户已存在,每次 OAuth 登录仍会同步这些字段:
|
||
|
||
- `area`
|
||
- `tenant_name`
|
||
- `dep_name`
|
||
- `dep_short_name`
|
||
- `ou_name`
|
||
|
||
对应:
|
||
|
||
- `app/routes/auth.py:518`
|
||
- `app/routes/auth.py:543`
|
||
- `app/routes/auth.py:552`
|
||
- `app/routes/auth.py:593`
|
||
|
||
### 结论
|
||
|
||
老系统对地区的真实定义是:
|
||
|
||
- 地区是组织主数据的一部分
|
||
- 地区由后端通过 MQ/组织系统同步
|
||
- 登录时会把地区同步进 `sso_users.area`
|
||
- 之后大量业务依赖这个 `area`
|
||
|
||
这说明:
|
||
|
||
- 老系统是“后端主数据决定地区”
|
||
- 不是“前端端口决定地区”
|
||
|
||
---
|
||
|
||
## 6. 用户主表 `sso_users` 的真实角色
|
||
|
||
从代码实际用法来看,`sso_users` 不只是登录表。
|
||
|
||
它承担了:
|
||
|
||
- 统一身份映射表
|
||
- 用户基础档案表
|
||
- 地区信息存储表
|
||
- 组织信息缓存表
|
||
- 登录失败次数 / 锁定状态记录表
|
||
|
||
常见使用字段:
|
||
|
||
- `id`
|
||
- `sub`
|
||
- `username`
|
||
- `nick_name`
|
||
- `phone_number`
|
||
- `email`
|
||
- `ou_id`
|
||
- `ou_name`
|
||
- `is_leader`
|
||
- `status`
|
||
- `deleted_at`
|
||
- `password`
|
||
- `try_count`
|
||
- `try_login_time`
|
||
- `area`
|
||
- `tenant_name`
|
||
- `dep_name`
|
||
- `dep_short_name`
|
||
|
||
对应代码位置:
|
||
|
||
- `app/routes/auth.py:451`
|
||
- `app/routes/auth.py:689`
|
||
- `app/routes/auth.py:914`
|
||
|
||
### 结论
|
||
|
||
老系统把 `sso_users` 作为了:
|
||
|
||
- 用户唯一主表
|
||
- 认证与组织信息的汇聚中心
|
||
|
||
这一点新系统应该继承。
|
||
|
||
---
|
||
|
||
## 7. 角色体系是什么
|
||
|
||
角色相关表:
|
||
|
||
- `roles`
|
||
- `user_role`
|
||
|
||
### 7.1 用户和角色关系
|
||
|
||
一个用户可以拥有多个角色。
|
||
|
||
角色查询通常通过:
|
||
|
||
- `user_role -> roles`
|
||
|
||
对应代码:
|
||
|
||
- `app/routes/auth.py:840`
|
||
- `app/routes/auth.py:852`
|
||
- `app/routes/auth.py:862`
|
||
|
||
### 7.2 OAuth 新用户默认角色
|
||
|
||
新用户默认自动分配:
|
||
|
||
- `common`
|
||
|
||
对应代码:
|
||
|
||
- `app/routes/auth.py:512`
|
||
|
||
### 7.3 角色本身带数据范围
|
||
|
||
老系统 `roles` 表本身带:
|
||
|
||
- `data_scope`
|
||
|
||
在 RBAC API 中大量出现:
|
||
|
||
- `RoleService.list_roles`
|
||
- `RoleService.get_role`
|
||
|
||
对应代码:
|
||
|
||
- `app/services/rbac/role_service.py:98`
|
||
- `app/services/rbac/role_service.py:171`
|
||
- `app/services/rbac/user_role_service.py:217`
|
||
|
||
### 结论
|
||
|
||
老系统角色不只是页面访问角色,它还是:
|
||
|
||
- 权限角色
|
||
- 数据范围角色
|
||
|
||
也就是说:
|
||
|
||
- 一个角色同时决定功能权限和数据权限
|
||
|
||
---
|
||
|
||
## 8. 菜单 / 路由权限模型
|
||
|
||
### 8.1 路由表和角色路由表
|
||
|
||
老系统页面菜单权限主要基于:
|
||
|
||
- `sys_routes`
|
||
- `role_route`
|
||
|
||
主逻辑在:
|
||
|
||
- `app/rbac/route_permission.py`
|
||
|
||
### 8.2 `get_user_routes(user_id)` 的真实行为
|
||
|
||
流程:
|
||
|
||
1. 查用户所有角色
|
||
2. 用角色查 `role_route`
|
||
3. join `sys_routes`
|
||
4. 只返回启用路由
|
||
5. 组装成树形结构
|
||
6. 还会把该路由相关的权限点附在 `permissions` 字段里
|
||
|
||
对应:
|
||
|
||
- `app/rbac/route_permission.py:22`
|
||
- `app/rbac/route_permission.py:47`
|
||
- `app/rbac/route_permission.py:63`
|
||
- `app/rbac/route_permission.py:100`
|
||
- `app/rbac/route_permission.py:127`
|
||
|
||
### 8.3 路由不仅是菜单,还带权限上下文
|
||
|
||
老系统会在返回路由树时,把页面对应的权限点也挂上去。
|
||
|
||
这意味着前端不是只拿“菜单”,而是拿:
|
||
|
||
- 菜单结构
|
||
- 页面权限上下文
|
||
|
||
这是很成熟的一种设计。
|
||
|
||
---
|
||
|
||
## 9. 功能权限模型
|
||
|
||
### 9.1 老系统不是只有 `role_permissions`
|
||
|
||
真正的功能权限核心是:
|
||
|
||
- `permissions`
|
||
- `role_permissions`
|
||
|
||
其中:
|
||
|
||
- `permissions`:权限定义表
|
||
- `role_permissions`:角色授权表
|
||
|
||
### 9.2 权限检查器 V2
|
||
|
||
代码:
|
||
|
||
- `app/rbac/permission_checker_v2.py`
|
||
|
||
它的逻辑是:
|
||
|
||
1. 查用户所有角色
|
||
2. 通过 `role_permissions` 找到权限
|
||
3. join `permissions`
|
||
4. 读 `permission_key`
|
||
5. 支持:
|
||
- 精确匹配
|
||
- 通配符权限
|
||
- `GRANT`
|
||
- `DENY`
|
||
|
||
对应:
|
||
|
||
- `app/rbac/permission_checker_v2.py:61`
|
||
- `app/rbac/permission_checker_v2.py:147`
|
||
- `app/rbac/permission_checker_v2.py:199`
|
||
|
||
### 9.3 权限键格式
|
||
|
||
权限键采用:
|
||
|
||
- `module:resource:action`
|
||
|
||
例如:
|
||
|
||
- `document:list:read`
|
||
- `document:delete:delete`
|
||
- `dify:dataset:read`
|
||
- `system:rbac:manage`
|
||
|
||
这套结构非常清晰,推荐新系统继续保留。
|
||
|
||
### 结论
|
||
|
||
老系统功能权限的真实设计是:
|
||
|
||
- `permissions.permission_key` 做标准权限定义
|
||
- `role_permissions` 决定角色拥有哪些权限
|
||
- 路由权限只是页面层,不能替代 `permissions`
|
||
|
||
---
|
||
|
||
## 10. 数据权限模型
|
||
|
||
这是老系统最关键但最容易被忽略的一层。
|
||
|
||
### 10.1 数据权限来源
|
||
|
||
数据权限核心在:
|
||
|
||
- `role_permissions.data_scope`
|
||
- `roles.data_scope`
|
||
- 用户 `area`
|
||
|
||
### 10.2 数据范围注入器
|
||
|
||
代码:
|
||
|
||
- `app/rbac/data_scope_injector_v2.py`
|
||
|
||
定义了三种数据范围:
|
||
|
||
- `ALL`
|
||
- `DEPT`
|
||
- `SELF`
|
||
|
||
对应:
|
||
|
||
- `app/rbac/data_scope_injector_v2.py:24`
|
||
|
||
### 10.3 三种范围含义
|
||
|
||
#### `ALL`
|
||
|
||
- 查看全部数据
|
||
- 不加过滤条件
|
||
|
||
#### `DEPT`
|
||
|
||
- 查看本地区数据
|
||
- 实现方式是:按 `area` 过滤
|
||
|
||
#### `SELF`
|
||
|
||
- 只能查看本人数据
|
||
- 实现方式是:按 `user_id` 过滤
|
||
|
||
对应:
|
||
|
||
- `app/rbac/data_scope_injector_v2.py:170`
|
||
- `app/rbac/data_scope_injector_v2.py:228`
|
||
|
||
### 10.4 本地区的真实含义
|
||
|
||
老系统里 `DEPT` 虽然名称叫部门范围,但实际很多地方是:
|
||
|
||
- 按地区 `area` 过滤
|
||
|
||
也就是说:
|
||
|
||
- 它更像“本地市范围”
|
||
- 而不是严格的部门树范围
|
||
|
||
这说明老系统的数据权限其实是:
|
||
|
||
- `ALL / 地区 / 本人`
|
||
|
||
而不是严格组织树。
|
||
|
||
---
|
||
|
||
## 11. `area` 在老系统中的真实地位
|
||
|
||
### 11.1 `area` 是核心业务字段
|
||
|
||
老系统中,`area` 被用于:
|
||
|
||
- 用户归属地区
|
||
- 数据权限过滤条件
|
||
- 业务记录写入默认地区
|
||
- Dify 知识库选择
|
||
- Dify 对话应用过滤
|
||
- 评查点地区隔离
|
||
|
||
### 11.2 Dify 知识库访问
|
||
|
||
代码:
|
||
|
||
- `app/routes/v3/dify_area_dataset.py`
|
||
|
||
逻辑:
|
||
|
||
- 普通用户:按 `current_user.area` 查本地区知识库
|
||
- 省级管理员:可看全部
|
||
|
||
对应:
|
||
|
||
- `app/routes/v3/dify_area_dataset.py:39`
|
||
- `app/routes/v3/dify_area_dataset.py:57`
|
||
- `app/routes/v3/dify_area_dataset.py:67`
|
||
|
||
### 11.3 Dify 对话应用过滤
|
||
|
||
代码:
|
||
|
||
- `app/routes/v3/dify_chat_apps.py`
|
||
|
||
逻辑:
|
||
|
||
- 根据 `current_user.area` 返回本地区应用
|
||
- 同时可以返回省级应用
|
||
|
||
对应:
|
||
|
||
- `app/routes/v3/dify_chat_apps.py:29`
|
||
- `app/routes/v3/dify_chat_apps.py:60`
|
||
|
||
### 11.4 PostgREST 转发层的 area 注入
|
||
|
||
代码:
|
||
|
||
- `app/exceptions/global_exc.py`
|
||
|
||
老系统在某些写操作中,甚至会自动把用户 `area` 写进业务数据。
|
||
|
||
典型例子:
|
||
|
||
- `evaluation_points` 表写入时自动填充 `area`
|
||
|
||
并且:
|
||
|
||
- `provincial_admin` 会被硬编码成 `省级`
|
||
|
||
对应代码:
|
||
|
||
- `app/exceptions/global_exc.py:250`
|
||
- `app/exceptions/global_exc.py:292`
|
||
- `app/exceptions/global_exc.py:307`
|
||
|
||
### 结论
|
||
|
||
在老系统中:
|
||
|
||
- `area` 不是附属字段
|
||
- 而是整套业务隔离体系的核心字段之一
|
||
|
||
---
|
||
|
||
## 12. 中间件与鉴权行为
|
||
|
||
### 12.1 JWT 中间件
|
||
|
||
代码:
|
||
|
||
- `app/middleware/jwt_auth.py`
|
||
|
||
行为:
|
||
|
||
- 白名单路径跳过
|
||
- 其他请求必须有 Bearer Token
|
||
- 中间件先做基础校验
|
||
- 然后将当前用户信息塞进:
|
||
- `request.state.current_user`
|
||
|
||
塞入字段包括:
|
||
|
||
- `user_id`
|
||
- `username`
|
||
- `nick_name`
|
||
- `email`
|
||
- `phone_number`
|
||
- `ou_id`
|
||
- `ou_name`
|
||
- `is_leader`
|
||
- `user_role`
|
||
- `area`
|
||
|
||
对应:
|
||
|
||
- `app/middleware/jwt_auth.py:49`
|
||
- `app/middleware/jwt_auth.py:75`
|
||
- `app/middleware/jwt_auth.py:88`
|
||
- `app/middleware/jwt_auth.py:89`
|
||
|
||
### 12.2 路由层再做细粒度权限校验
|
||
|
||
真正功能权限由:
|
||
|
||
- `require_permission_v2`
|
||
|
||
处理,位置:
|
||
|
||
- `app/rbac/decorators_v2.py`
|
||
|
||
它会:
|
||
|
||
- 从 `request.state.current_user` 取 `user_id`
|
||
- 调 `PermissionCheckerV2.check_permission`
|
||
- 无权限则 403
|
||
|
||
对应:
|
||
|
||
- `app/rbac/decorators_v2.py:56`
|
||
- `app/rbac/decorators_v2.py:92`
|
||
|
||
### 结论
|
||
|
||
老系统是两层鉴权:
|
||
|
||
- 中间件:验 token
|
||
- 装饰器/依赖:验功能权限
|
||
|
||
这个分层设计是合理的。
|
||
|
||
---
|
||
|
||
## 13. 老系统角色体系的实际业务语义
|
||
|
||
根据代码表现,老系统大致存在以下角色层次:
|
||
|
||
- `provincial_admin`
|
||
- `admin`
|
||
- `common`
|
||
- 以及其他业务角色
|
||
|
||
### 13.1 `provincial_admin`
|
||
|
||
特点:
|
||
|
||
- 高权限角色
|
||
- 数据范围通常为 `ALL`
|
||
- 某些业务场景会被特殊处理为“省级”
|
||
- 可访问全部地区的数据 / 配置
|
||
|
||
体现位置:
|
||
|
||
- `app/exceptions/global_exc.py:292`
|
||
- `app/routes/v3/dify_area_dataset.py:67`
|
||
|
||
### 13.2 `admin`
|
||
|
||
特点:
|
||
|
||
- 更像市级管理员
|
||
- 数据范围通常为本地区
|
||
- 很多场景按 `area` 做限制
|
||
|
||
体现位置:
|
||
|
||
- `app/services/rbac/user_role_service.py:91`
|
||
- `app/exceptions/global_exc.py:229`
|
||
|
||
### 13.3 `common`
|
||
|
||
特点:
|
||
|
||
- 默认普通用户
|
||
- 新用户自动分配
|
||
- 权限最低
|
||
- 数据范围通常为 `SELF` 或较弱的本地区权限
|
||
|
||
体现位置:
|
||
|
||
- `app/routes/auth.py:512`
|
||
- `app/routes/auth.py:601`
|
||
|
||
---
|
||
|
||
## 14. 老系统真实的架构图
|
||
|
||
如果按实际行为抽象,老系统可以概括为:
|
||
|
||
### 14.1 用户身份层
|
||
|
||
- `sso_users`
|
||
- `sub`
|
||
- OAuth / 本地账密登录
|
||
|
||
### 14.2 角色层
|
||
|
||
- `roles`
|
||
- `user_role`
|
||
|
||
### 14.3 页面菜单层
|
||
|
||
- `sys_routes`
|
||
- `role_route`
|
||
|
||
### 14.4 功能权限层
|
||
|
||
- `permissions`
|
||
- `role_permissions`
|
||
- `permission_key`
|
||
- `grant_type`
|
||
|
||
### 14.5 数据权限层
|
||
|
||
- `role_permissions.data_scope`
|
||
- `roles.data_scope`
|
||
- 用户 `area`
|
||
- 业务表 `area`
|
||
|
||
### 14.6 组织归属层
|
||
|
||
- `ou_id`
|
||
- `ou_name`
|
||
- `tenant_name`
|
||
- `dep_name`
|
||
- `dep_short_name`
|
||
- 通过 MQ / 组织表同步
|
||
|
||
### 结论
|
||
|
||
老系统不是简单 RBAC,而是:
|
||
|
||
- `RBAC + 地区数据隔离 + 组织同步`
|
||
|
||
---
|
||
|
||
## 15. 老系统优点
|
||
|
||
### 15.1 登录入口统一
|
||
|
||
OAuth 和账密最终走同一条认证主链。
|
||
|
||
### 15.2 用户主数据集中
|
||
|
||
所有用户最终都汇总到:
|
||
|
||
- `sso_users`
|
||
|
||
### 15.3 路由权限设计成熟
|
||
|
||
- `sys_routes + role_route`
|
||
- 支持树形菜单
|
||
- 支持隐藏路由
|
||
- 支持角色路由缓存
|
||
|
||
### 15.4 功能权限设计成熟
|
||
|
||
- `permissions`
|
||
- `role_permissions`
|
||
- `grant / deny`
|
||
- 通配符匹配
|
||
|
||
### 15.5 数据范围是正式模型,不是临时过滤
|
||
|
||
虽然比较粗糙,但已经有:
|
||
|
||
- `ALL`
|
||
- `DEPT`
|
||
- `SELF`
|
||
|
||
### 15.6 地区来自后端主数据
|
||
|
||
老系统地区来源比“前端端口推断”可靠得多。
|
||
|
||
---
|
||
|
||
## 16. 老系统缺点
|
||
|
||
### 16.1 `area` 过载过重
|
||
|
||
`area` 同时承担:
|
||
|
||
- 用户地区
|
||
- 数据权限过滤条件
|
||
- 业务默认地区
|
||
- 规则隔离条件
|
||
- 知识库分配条件
|
||
|
||
语义过重。
|
||
|
||
### 16.2 数据范围表达力不足
|
||
|
||
只有三档:
|
||
|
||
- `ALL`
|
||
- `DEPT`
|
||
- `SELF`
|
||
|
||
无法优雅表达:
|
||
|
||
- 多地区访问
|
||
- 指定地区集合
|
||
- 复杂跨区权限
|
||
|
||
### 16.3 大量业务写死 `area` 逻辑
|
||
|
||
导致:
|
||
|
||
- 架构耦合重
|
||
- 后续改动成本高
|
||
- 组织逻辑与业务逻辑混杂
|
||
|
||
### 16.4 存在角色硬编码
|
||
|
||
例如:
|
||
|
||
- `provincial_admin -> 省级`
|
||
|
||
这类逻辑写死在业务层,不够优雅。
|
||
|
||
### 16.5 `DEPT` 实际上更像地区范围,不是部门范围
|
||
|
||
命名容易误导。
|
||
|
||
---
|
||
|
||
## 17. 对新系统的启示
|
||
|
||
## 17.1 必须继承的部分
|
||
|
||
新系统应继承老系统的这些优点:
|
||
|
||
- 统一登录入口
|
||
- OAuth / 账密统一落主用户表
|
||
- `sys_routes + role_route`
|
||
- `permissions + role_permissions`
|
||
- 标准 `permission_key`
|
||
- `grant / deny`
|
||
- 数据范围模型概念
|
||
- 组织同步决定地区
|
||
|
||
## 17.2 必须升级的部分
|
||
|
||
新系统不能原样照搬老系统的缺点。
|
||
|
||
必须升级为:
|
||
|
||
- 不再只有一个 `area` 字段承载所有数据权限语义
|
||
- 把用户默认地区和可访问地区分开
|
||
- 把数据权限从 `DEPT` 升级为正式 `region scope`
|
||
- 把组织同步保留,但不要在各业务层到处散写地区逻辑
|
||
|
||
---
|
||
|
||
## 18. 新系统设计应如何继承老系统
|
||
|
||
从老系统出发,新系统最合理的方向不是推翻,而是升级:
|
||
|
||
### 18.1 保留骨架
|
||
|
||
- `sso_users`
|
||
- `roles`
|
||
- `user_role`
|
||
- `sys_routes`
|
||
- `role_route`
|
||
- `permissions`
|
||
- `role_permissions`
|
||
|
||
### 18.2 替换数据权限实现
|
||
|
||
将老系统:
|
||
|
||
- `ALL / DEPT / SELF`
|
||
|
||
升级为:
|
||
|
||
- `ALL`
|
||
- `SELF`
|
||
- `HOME_REGION`
|
||
- `CUSTOM_REGIONS`
|
||
- `PROVINCIAL`
|
||
|
||
### 18.3 组织同步仍保留
|
||
|
||
地区来源仍然应该以后端组织主数据为准,而不是前端推断。
|
||
|
||
### 18.4 将 `area` 升级为正式 `region_code`
|
||
|
||
业务表中的地区字段建议统一收口成稳定 code,而不是直接到处使用中文字符串。
|
||
|
||
---
|
||
|
||
## 19. 一句话总结
|
||
|
||
老系统 `docauditai` 的真实用户权限架构不是“6 张表的简单角色系统”,而是:
|
||
|
||
- 以 `sso_users` 为用户主数据中心
|
||
- 通过 OAuth / 账密统一登录
|
||
- 通过组织系统同步 `area / tenant / dep`
|
||
- 用 `roles + user_role + sys_routes + role_route + permissions + role_permissions`
|
||
管功能权限
|
||
- 用 `data_scope + area`
|
||
管数据权限
|
||
|
||
本质是:
|
||
|
||
- `RBAC + 地区数据权限 + 组织同步`
|
||
|
||
这就是新系统设计时真正应该继承的老逻辑骨架。
|
||
|