20 KiB
FMS 权限体系、字段权限与数据范围设计
0. 总体约定
本系统是 SQL 驱动的内部 ERP,权限表和模块配置表主要用于生成通用查询、写入和界面配置规则。
权限层次
| 类别 | 回答的问题 | 存储 | 示例 |
|---|---|---|---|
| 菜单入口权限 | 能不能显示菜单并进入对应页面 | s_user_power(权限点编码) |
menu.cw_fee_list.access |
| 功能权限 | 进入页面后能不能执行某个动作 | s_user_power(权限点编码) |
action.cw_fee.delete — 能不能删除 |
| 数据范围 | 能对哪些行做 | s_user_data_scope(拼 SQL 行条件) |
只能删本部门的 |
| 字段权限 | 能看到 / 改哪些列 | s_user_field_permission |
看不到内部成本列 |
菜单是页面的一部分。菜单入口权限同时控制菜单显示、路由进入和页面初始化接口;没有该权限时,用户不能通过直接输入 URL 进入页面。create / update / delete / export 以及 audit / settle 等业务动作属于功能权限;数据范围只描述行的可见与可操作边界,字段权限只描述列的访问能力。
最终权限是交集关系:
菜单入口权限 ∩ 功能权限 ∩ 数据范围 ∩ 字段权限
权限编码规范
权限点编码即 s_power.b_id,也是 s_user_power.b_power_id 的外键值。格式为 类型.对象[.能力],用点号分隔层级,全小写:
| 类型 | 编码 | 含义 | 生成时机 |
|---|---|---|---|
menu |
menu.<菜单编码>.access |
菜单显示及对应页面进入 | 保存菜单时自动生成 |
module |
module.<模块编码>.access |
模块数据访问(通常对应 read) | 模块启用时自动生成 |
action |
action.<模块编码>.<操作或动作> |
数据操作 create / update / delete / export,以及业务动作 audit / settle 等 |
模块启用或动作注册时自动生成 |
约定:
- 点号分隔层级,从左到右依次为
类型→对象→能力; - 对象编码本身不允许包含
.:模块和菜单编码统一不含点号,否则无法按段解析; _只用于编码内部(如container_main),不做分隔符;- 不使用
/、#、|、:(与路由冲突或需转义); - 最长编码为
action.container_main.cancel_settle(33 字符),b_id预留varchar(100); - 编码生成后不可编辑,需要变更时删除重建;
- 判断权限时使用完整编码(前端用 Set 判断,零 join);查询时可用前缀,如
b_id LIKE 'action.cw_fee.%'取某模块全部动作;注意_在LIKE中是单字符通配符,前缀查询时需用ESCAPE '\'转义或改用LEFT(b_id, n)比较。
权限采用“入口和功能默认拒绝、字段使用模块默认配置并允许用户收紧、数据范围使用模块默认或操作级明确配置”的策略:
menu.<菜单编码>.access同时表示菜单显示和页面进入,必须存在有效的用户授权记录;不再单独定义page.*或report.*入口权限;module.<模块编码>.access表示模块访问;模块级数据操作统一编码为action.<模块编码>.<操作>;功能权限没有有效授权记录即无权限,s_power或用户授权记录存在b_canuse = 0时明确禁用;- 字段没有有效的用户字段策略时,按模块字段默认配置执行,不额外限制查询、编辑和导出;有效字段策略只能收紧权限;
- 数据范围优先使用操作级
override,没有override再使用模块默认的*范围;模块必须由 IT 明确配置默认范围,未配置时拒绝访问;同一层级多条范围按 OR 合并; - 用户个性化只影响列表布局、查询收起态、顺序和宽度,不得越过字段权限、字段启用状态或数据范围;
- 数据库层不建 check 约束,字段取值及组合规则由业务层校验(见各表下方「业务层约束」)。
权限过滤、字段裁剪和数据范围条件最终必须落实到查询 SQL 中,前端隐藏菜单、按钮和字段只用于界面展示。
当前落地方式(阶段性):后端是通用数据网关,/data/loaddata 直接接受 search_condition(行过滤)与 search_columns(列裁剪),自身不做权限判断。因此本阶段由前端拼装 SQL 条件实现权限过滤:
- 数据范围 → 拼进
search_condition; - 字段
hidden策略 → 从search_columns中剔除; - 拼装逻辑收敛在前端唯一的纯函数工具里(不散落在各页面),后续后端具备能力时可原样下沉;
- 这只是执行位置的前移,不改变任何权限规则;前端拼装属于界面层控制,敏感数据仍应在具备条件后补充后端校验。
资源与权限归属
权限以菜单入口和业务模块为核心,不以 Vue 路由或组件为核心:
| 资源 | 主要职责 | 是否授予模块数据权限 |
|---|---|---|
menu |
菜单显示、页面入口和路由访问 | 否 |
module |
模块访问、字段和数据范围;数据操作以 action.<模块编码>.<操作> 表示 |
是 |
action |
新增、修改、删除、导出,以及审核、反审核、核销等业务动作 | 通常绑定业务模块 |
| Vue 路由/组件 | 技术实现和页面复用 | 否 |
- 菜单权限不自动授予所使用模块的操作权限;
- 一个菜单页面可以使用多个模块,一个模块可以被多个菜单页面复用;
- Vue 路由和组件不是权限标识,同一公共 Vue 页面可以被多个菜单使用;
s_menu.b_module_id不能表达多模块关系,应由s_menu_module维护菜单与模块的多对多关系。
复合页面和公共页面
以海运详细页为例:
海运详细页
├── sea_main
├── container_main
├── container_detail
├── finance_main
└── finance_detail
技术人员按菜单配置一次“页面使用哪些模块”的关系。页面打开后,各模块分别检查自己的访问权限、字段权限和数据范围:
- 必需模块没有读取权限时,页面不能完成正常业务,可以拒绝进入;
- 非必需模块没有读取权限时,隐藏对应页签或区域;
- 菜单权限与模块权限不自动继承;
- 同一个公共 Vue 页面被多个菜单使用时,以当前菜单编码和实际
module_id判断权限,不以路由名称或组件名称判断权限。
动作权限按业务模块归属配置,统一使用 action.<module_id>.<action_id>;不把动作绑定到 Vue 路由或公共组件。确实只对某个菜单页面生效的动作,可在 s_menu_module 中配置页面能力上限,再与用户的模块动作权限取交集。
权限计算顺序
一个页面或接口按以下顺序计算最终权限:
- 用户账号和对应资源是否启用;
- 菜单入口是否有有效
menu.<菜单编码>.access授权;无权限时菜单不显示且页面不可进入; - 当前动作是否有有效
action.<模块>.<动作>授权,并且该动作在s_menu_module中被当前页面允许; - 当前模块操作是否被
s_power或s_user_power.b_canuse = 0明确禁用;无有效授权记录即无权限; - 根据模块字段元数据和用户字段策略裁剪字段、查询和导出能力(当前由前端从
search_columns中剔除隐藏列); - 根据数据范围的
override、default或all生成 SQL 行条件(当前由前端拼装进search_condition); - 在以上结果基础上应用用户列表布局和查询布局偏好。
页面显示权限和接口执行权限必须使用同一套计算规则。前端可以提前隐藏无权限内容,但不能代替后端 SQL 的最终判断。
1. 表结构
权限相关表的完整 DDL、索引与业务层约束统一维护在《FMS新系统核心表结构设计.md》「8. 权限」;用户个性化偏好表见同文档「4. 用户个性化」。本章只保留设计说明与计算规则。
1.1 s_menu_module(菜单-模块关系表)
菜单页面与业务模块是多对多关系。菜单记录既表示导航入口,也表示对应的逻辑页面;Vue 路由和公共组件只作为技术实现,不参与权限标识。
业务层约束:
b_menu_code、b_module_id必须引用已启用的菜单和模块;b_allow_*表示 IT 为该页面配置的能力上限,不代表用户已经获得权限;- 用户最终能力为页面上限与用户功能权限的交集;
b_required = 1的模块没有访问权限时,页面可以拒绝进入;非必需模块可隐藏对应区域;- 同一模块可被多个菜单复用,同一菜单也可使用多个模块。
1.2 s_power(权限定义表)
定义见《FMS新系统核心表结构设计.md》「8. 权限」。
1.3 s_user_power(用户权限授权表)
定义见《FMS新系统核心表结构设计.md》「8. 权限」。
1.4 s_user_field_permission(用户字段访问策略表)
定义见《FMS新系统核心表结构设计.md》「8. 权限」。
1.5 s_user_data_scope(用户数据范围授权表)
定义见《FMS新系统核心表结构设计.md》「8. 权限」。
数据范围计算规则:
- 先查询当前操作的
override记录;存在时只使用override,忽略模块默认范围; - 没有
override时使用operation = '*'的default记录; - 默认范围和操作覆盖都没有配置时,按
all处理,不追加数据行过滤条件; - 同一层级存在多条范围记录时按 OR 合并;
b_scope_no只用于区分同级范围行; read、export、update、delete在 SQL 行条件中应用范围;create在写入前校验新增数据是否符合范围。
配置流程
技术人员按以下顺序配置,避免在每个页面重复维护权限:
- 配置模块、字段和模块默认数据范围;
- 配置菜单、Vue 模板及
s_menu_module菜单-模块关系; - 由系统按菜单、模块和动作定义生成
s_power; - 在用户授权界面按菜单展示入口、模块动作、字段限制和数据范围;
- 客户通常只需要勾选可进入的菜单和可执行的动作,再配置敏感字段和数据范围限制。
客户按菜单授权,不需要理解路由或 Vue 组件。菜单下展示该页面使用的模块和可用操作,用户只保存明确授予的 s_user_power.b_canuse = 1 记录;字段和数据范围保存用户级限制或操作级范围配置。
菜单与模块的多对多关系由 s_menu_module 维护。菜单页面使用同一模块时,模块定义只维护一次;页面允许的操作由关系表的 b_allow_* 配置限制。
当前不单独引入 s_feature:菜单记录已经承担逻辑页面入口的职责,Vue 公共页面通过菜单的模板配置复用;只有将来出现脱离菜单的可授权功能或需要独立维护的页面区域时,才重新评估是否增加功能实例对象。
1.6 s_user_field_pref(非权限:用户列表布局偏好表)
定义见《FMS新系统核心表结构设计.md》「4. 用户个性化」。
1.7 s_user_query_field_pref(非权限:用户查询布局偏好表)
定义见《FMS新系统核心表结构设计.md》「4. 用户个性化」。
用户个性化规则:
s_user_field_pref区分查看列表和可编辑列表两套布局;s_user_query_field_pref只影响普通查询收起态,展开态和高级查询仍使用所有有效查询字段;- 没有个人偏好时,回落到模块默认的
s_field_view、s_field_edit、s_field_query配置; - 个人偏好只能调整可见字段的显示、顺序和宽度,不能显示隐藏字段、启用停用字段或扩大查询/导出权限;
- “恢复默认”删除当前用户、当前模块对应的个人偏好记录。
2. Demo 数据
示例沿用 cw_fee 费用模块。示例用户:张三(费用会计)、李四(财务经理)。
示例菜单与模块关系:
| b_menu_code | b_module_id | b_usage_type | b_required | b_allow_read | b_allow_create | b_allow_update | b_allow_delete | b_allow_export |
|---|---|---|---|---|---|---|---|---|
| sea_fee_list | cw_fee | main | 1 | 1 | 0 | 0 | 0 | 1 |
| sea_fee_edit | cw_fee | main | 1 | 1 | 1 | 1 | 0 | 0 |
费用列表和费用编辑使用同一个 cw_fee 模块,但页面能力上限不同;用户最终能力还要与自己的功能权限取交集。
2.1 s_power(权限定义)
| b_id | b_name | b_power_type | b_owner_type | b_owner_id | b_capability | b_operation | b_canuse | b_xh |
|---|---|---|---|---|---|---|---|---|
| menu.cw_fee_list.access | 费用列表菜单 | menu | menu | cw_fee_list | access | access | 1 | 10 |
| menu.cw_fee_edit.access | 费用编辑菜单 | menu | menu | cw_fee_edit | access | access | 1 | 20 |
| module.cw_fee.access | 费用模块访问 | module | module | cw_fee | access | access | 1 | 30 |
| action.cw_fee.create | 费用新增 | action | module | cw_fee | create | execute | 1 | 40 |
| action.cw_fee.update | 费用修改 | action | module | cw_fee | update | execute | 1 | 50 |
| action.cw_fee.delete | 费用删除 | action | module | cw_fee | delete | execute | 1 | 60 |
| action.cw_fee.export | 费用导出 | action | module | cw_fee | export | execute | 1 | 70 |
| action.cw_fee.audit | 费用审核 | action | module | cw_fee | audit | execute | 1 | 80 |
| action.cw_fee.unaudit | 费用反审核 | action | module | cw_fee | unaudit | execute | 1 | 90 |
2.2 s_user_power(用户授权)
| b_user_id | b_power_id | b_canuse | b_updatedatetime |
|---|---|---|---|
| zhangsan | menu.cw_fee_list.access | 1 | 2026-02-01 09:00:00 |
| zhangsan | menu.cw_fee_edit.access | 1 | 2026-02-01 09:00:00 |
| zhangsan | module.cw_fee.access | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.update | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.export | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.audit | 1 | 2026-02-01 09:00:00 |
| lisi | menu.cw_fee_list.access | 1 | 2026-01-15 10:00:00 |
| lisi | menu.cw_fee_edit.access | 1 | 2026-01-15 10:00:00 |
| lisi | module.cw_fee.access | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.create | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.update | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.delete | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.export | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.audit | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.unaudit | 1 | 2026-01-15 10:00:00 |
张三没有 action.cw_fee.create / action.cw_fee.delete 权限。由于统一采用默认拒绝策略,没有授权记录即无权限,不需要额外保存禁止记录。
2.3 s_user_field_permission(字段访问策略)
| b_user_id | b_module_id | b_field | b_access_mode | b_allow_query | b_allow_export | b_canuse |
|---|---|---|---|---|---|---|
| zhangsan | cw_fee | mx_amount | view | 1 | 0 | 1 |
| zhangsan | cw_fee | mx_internal_cost | hidden | 0 | 0 | 1 |
| lisi | cw_fee | mx_amount | edit | 1 | 1 | 1 |
| lisi | cw_fee | mx_internal_cost | view | 1 | 0 | 1 |
张三对金额字段是“可见、只读、可查询、不可导出”,对内部成本字段完全不可见;李四可以编辑和导出金额,但内部成本字段仍然只读。
未配置用户字段策略的字段按模块默认配置执行,不额外限制查询、编辑和导出;上表中的记录只表示对特定字段的收紧。
2.4 s_user_data_scope(用户数据范围授权)
| b_user_id | b_module_id | b_operation | b_scope_level | b_scope_type | b_scope_field | b_scope_value | b_scope_no | b_updatedatetime |
|---|---|---|---|---|---|---|---|---|
| zhangsan | cw_fee | * | default | department | b_department_id | null | 1 | 2026-02-01 09:00:00 |
| zhangsan | cw_fee | update | override | own | b_inputuser_id | null | 1 | 2026-02-01 09:00:00 |
| lisi | cw_fee | * | default | department_tree | b_department_id | null | 1 | 2026-01-15 10:00:00 |
| lisi | cw_fee | delete | override | all | null | null | 1 | 2026-01-15 10:00:00 |
- 张三使用模块默认范围(
*+ department)读取本部门数据,update 有操作级覆盖(override + own),只能修改本人录入的费用; - 李四使用默认范围 department_tree 处理一般操作,delete 有操作级覆盖为 all,可删全模块数据。
3. 权限在哪里配置
权限分「定义端」和「授权端」,两者分离:
| 位置 | 配置内容 | 写入的表 |
|---|---|---|
| 模块管理(IT) | 维护业务模块、字段默认配置和默认数据范围;自动生成 module.<模块编码>.access 与 action.<模块编码>.<操作或动作> |
s_module、s_field、s_power |
| 菜单管理(IT) | 维护菜单层级、Vue 模板和菜单-模块关系;保存菜单时自动生成 menu.<菜单编码>.access |
s_menu、s_menu_module、s_power |
| 用户授权(客户) | 按菜单树勾选入口权限;在菜单下查看其模块并配置功能动作、字段策略和数据范围 | s_user_power、s_user_field_permission、s_user_data_scope |
| 个人偏好(非权限) | 列可见 / 顺序 / 宽度、查询收起态字段 | s_user_field_pref、s_user_query_field_pref |
规则:
- 动作归属模块而非菜单,同一个动作只配置一次,不随菜单重复;
- 勾选菜单只授予入口权限,不自动授予模块操作权限;
- 客户授权界面按菜单展示模块,但落库仍分别写入菜单权限、模块动作权限、字段权限和数据范围;
s_menu_module.b_allow_*是 IT 配置的页面能力上限,用户实际能力取其与s_user_power的交集;- 菜单
b_canuse = 0时一票否决,所有用户均不可见。
现状提示:当前系统只有菜单管理维护权限点字典,
s_user_power等授权表尚未建立,也没有授权界面;权限过滤目前只有登录态校验,其余全部未落地。
客户授权页面
客户只按菜单树授权,不需要理解 Vue 路由、公共组件或数据库表。选中一个菜单后,右侧展示该菜单通过 s_menu_module 使用的模块,并提供:
菜单入口:显示菜单、进入页面
模块能力:查看、新增、修改、删除、导出、业务动作
字段策略:隐藏、只读、可编辑、查询、导出
数据范围:本人、部门、部门树、自定义等
勾选菜单只授予入口权限;模块动作、字段权限和数据范围按当前菜单下的模块分组展示,但按各自的表独立落库。
IT 统一配置页面
IT 在统一配置中心维护模块、字段、动作、菜单模板和 s_menu_module 关系:
- 维护业务模块及字段默认配置;
- 维护菜单层级、对应 Vue 模板和页面使用的模块;
- 为模块注册新增、修改、删除、导出、审核等动作;
- 配置每个菜单-模块关系的必需性和页面能力上限;
- 由系统自动生成
s_power,客户授权页面只消费这些定义。
4. 实施约定
- 入口和功能权限统一采用默认拒绝策略:没有有效授权记录即无权限;权限点或授权记录为
b_canuse = 0时明确禁用。 - 模块必须配置默认字段策略和默认数据范围;字段用户配置只允许进一步收紧,数据范围按模块默认或操作级明确配置计算。
own/department/department_tree等数据范围所需的用户部门信息,必须由登录用户资料或后端用户服务提供。- 当前阶段如果只能由前端拼装
search_condition和search_columns,必须将逻辑集中在唯一工具中;后端或数据网关具备能力后,应把菜单、功能、字段和数据范围校验下沉到服务端。 - 角色是否作为用户授权的批量模板另行设计,不改变本文件的菜单-模块-字段-数据范围模型。
5. 用户个性化(非权限,附录)
s_user_field_pref 和 s_user_query_field_pref 只保存列表布局和查询布局偏好,不参与权限计算。它们只能在已经通过菜单、功能、字段和数据范围校验后生效,不能显示被字段权限隐藏的字段,也不能扩大查询、导出或数据范围。