














升鲜宝供应链系统
统一字段权限功能设计方案
适用范围:Spring Boot + MyBatis-Plus + Vue3 + Redis/Caffeine
覆盖:后台管理端 / 小程序 / App / POS / 导入导出 / 打印 / PDF / 图片 / 分享码
文档版本:V1.0 Final
生成日期:2024-04-28
文档性质:研发可落地设计文档
适用项目:升鲜宝供应链管理系统
|
版本 |
日期 |
说明 |
适用阶段 |
|
V1.0 Final |
2024-04-28 |
统一字段权限最终完善版,面向一次性底座重构与分批业务接入 |
设计评审 / 研发落地 |
|
适配范围 |
长期有效 |
适配升鲜宝供应链管理系统后台、小程序、App、POS、导入导出、打印、分享码等场景 |
全系统 |
目录生成提示:在 Word 中右键更新域即可生成完整目录。
提示:Word 打开后可右键目录区域选择“更新域”,即可生成真实页码目录。本文档同时提供完整章节标题,便于直接阅读。
最终设计结论
升鲜宝字段权限不能只做“前端隐藏列”。最终应建设为统一字段安全底座,覆盖列表、详情、表单、新增、修改、审核、反审核、查询、排序、导入导出、打印、PDF/图片生成、分享码外部查看等全链路场景。
统一字段权限组件的目标,是让不同用户、不同角色、不同门店、不同终端、不同业务场景下,同一个资源的字段能够按照统一规则完成可见、脱敏、隐藏、禁止、可编辑、可查询、可导出、可打印、可分享等控制。
|
目标 |
说明 |
|
统一建模 |
字段资源、字段定义、字段场景、字段策略、脱敏规则、日志审计全部统一管理。 |
|
统一接入 |
业务模块通过注解、API绑定或公共组件接入,避免每个模块重复写字段权限逻辑。 |
|
统一拦截 |
后端对返回值、提交DTO、查询字段、排序字段、导出字段、打印字段、分享字段进行强校验。 |
|
统一审计 |
敏感字段访问、字段脱敏、越权查询、越权编辑、批量导出、分享码访问等全部记录日志。 |
|
统一扩展 |
后续可扩展到 SQL 字段裁剪、动态列、字段审批、字段水印、字段级数据血缘。 |
|
权限类型 |
控制对象 |
典型示例 |
与字段权限关系 |
|
菜单权限 |
能否进入页面 |
是否能进入商品管理 |
字段权限在菜单权限通过后生效 |
|
按钮权限 |
能否执行动作 |
是否能新增、审核、反审核 |
字段编辑权限在按钮动作内部继续细分 |
|
数据权限 |
能看到哪些数据行 |
只能看自己门店、自己客户 |
字段权限控制同一行数据中的字段范围 |
|
字段权限 |
能看/改/查/导出哪些字段 |
是否能看成本价、手机号、余额 |
本方案核心 |
|
场景权限 |
同一资源在不同场景的差异 |
商品在销售开单与商品管理中不同 |
字段权限必须绑定 scene_code |
|
业务场景 |
权限痛点 |
最终解决方式 |
|
商品列表 |
普通业务员不应查看成本价、毛利率、库存金额 |
READ 权限返回过滤,EXPORT/PRINT/SHARE 单独控制 |
|
销售开单选择商品 |
需要商品名称、规格、售价,但不能看到采购价、成本价 |
同一 pms.goods 资源使用 admin.sales.order.create.selectGoods 场景 |
|
销售出库 |
仓库人员需要库位、批次、库存数量,不需要客户授信、账期 |
WMS 场景只开放仓储相关字段 |
|
客户列表 |
手机号、地址、账期、授信额度、应收余额需要不同权限 |
手机号脱敏,账期/授信/余额默认高敏隐藏 |
|
供应商模块 |
采购可看采购价和联系人,仓管不应看到结算账户 |
角色/岗位/部门组合授权,CRITICAL 字段默认禁止 |
|
Excel 导出 |
页面可见不代表可以导出 |
EXPORT 权限独立计算,敏感字段可配置审批 |
|
打印配送单 |
客户单据不能暴露成本、采购价、内部备注 |
PRINT/RENDER 权限过滤模板字段和数据 |
|
分享码查看 |
外部客户/供应商/司机不能继承内部员工权限 |
SHARE 独立场景,外部最小字段集 |
图 1 统一字段权限总体架构
整体架构分为四层:前端 Schema 层、FieldPermission API 层、FieldPermissionEngine 引擎层、业务集成层。业务模块不直接写权限判断,而是通过公共入口接入字段权限底座。
|
组件 |
职责 |
落地内容 |
|
字段资源中心 |
定义业务资源 |
resource_code、资源名称、领域、主表、VO 类 |
|
字段定义中心 |
定义资源字段 |
field_code、field_path、db_column、敏感等级、默认策略 |
|
业务场景中心 |
定义场景差异 |
scene_code、终端、页面、动作、场景类型 |
|
字段策略中心 |
配置主体授权 |
用户/角色/部门/岗位/门店/租户的字段权限 |
|
脱敏规则中心 |
统一脱敏算法 |
手机号、邮箱、身份证、银行卡、地址、金额区间 |
|
字段权限引擎 |
计算最终权限 |
上下文、策略匹配、优先级合并、缓存、日志 |
|
业务接入层 |
全链路拦截 |
AOP、ResponseAdvice、QueryFacade、ExportGuard、PrintGuard、ShareGuard |
图 2 字段权限运行时处理流程
字段权限的本质公式如下:
最终字段权限 =
租户 + 用户 + 角色 + 部门 + 岗位 + 门店
+ 资源编码 resource_code
+ 场景编码 scene_code
+ 字段编码 field_code
+ 操作类型 operation_type
+ 字段敏感等级 sensitivity_level
+ 默认策略 + 场景策略 + 授权策略 + 显式拒绝策略
tenant_id
+ resource_code
+ scene_code
+ field_code
+ operation_type
+ subject_type
+ subject_id
|
示例维度 |
示例值 |
|
租户 |
10001 |
|
资源 |
pms.goods |
|
场景 |
admin.sales.order.create.selectGoods |
|
字段 |
costPrice |
|
操作 |
READ |
|
主体 |
ROLE:销售员 |
|
结果 |
HIDE |
|
操作类型 |
说明 |
典型场景 |
|
READ |
读取字段 |
列表、详情、下拉选择、看板 |
|
CREATE |
新增字段提交 |
新增客户时提交授信额度 |
|
UPDATE |
修改字段提交 |
修改商品采购价、成本价 |
|
QUERY |
查询条件字段 |
高级查询使用成本价、余额过滤 |
|
SORT |
排序字段 |
按毛利率、余额排序 |
|
IMPORT |
导入字段 |
Excel 导入客户账期、商品成本 |
|
EXPORT |
导出字段 |
Excel 导出手机号、成本价、余额 |
|
|
打印字段 |
打印配送单、对账单、采购单 |
|
RENDER |
PDF/图片生成字段 |
小票图片、单据 PDF、分享图 |
|
SHARE |
分享码外部查看字段 |
客户扫码、供应商扫码、司机任务 |
|
权限类型 |
模式 |
说明 |
|
读取 read_mode |
PLAIN |
原值返回 |
|
读取 read_mode |
MASK |
脱敏返回 |
|
读取 read_mode |
HIDE |
字段隐藏,返回 null 或移除 |
|
读取 read_mode |
FORBIDDEN |
禁止访问,记录越权日志 |
|
编辑 edit_mode |
EDITABLE |
可编辑 |
|
编辑 edit_mode |
READONLY |
只读,不允许提交变更 |
|
编辑 edit_mode |
FORBIDDEN |
禁止提交 |
|
查询 query_mode |
ALLOW |
允许作为查询条件 |
|
查询 query_mode |
MASK_QUERY |
仅允许脱敏查询,例如手机号后四位 |
|
查询 query_mode |
FORBIDDEN |
禁止查询 |
|
导出 export_mode |
PLAIN / MASK / FORBIDDEN / APPROVAL_REQUIRED |
原值、脱敏、禁止、审批后导出 |
|
输出 print/render/share |
ALLOW / MASK / FORBIDDEN |
允许、脱敏、禁止 |
|
等级 |
编码 |
默认读取 |
默认导出 |
默认分享 |
示例字段 |
|
1 |
PUBLIC |
PLAIN |
PLAIN |
ALLOW |
商品名称、规格、单位 |
|
2 |
INTERNAL |
PLAIN |
PLAIN |
按场景 |
库存数量、订单金额 |
|
3 |
SENSITIVE |
MASK |
MASK |
MASK/FORBIDDEN |
手机号、地址、联系人 |
|
4 |
SECRET |
HIDE |
FORBIDDEN |
FORBIDDEN |
成本价、毛利率、应收余额 |
|
5 |
CRITICAL |
FORBIDDEN |
FORBIDDEN |
FORBIDDEN |
银行账号、身份证、接口密钥 |
建议格式:领域.资源,例如 pms.goods、crm.customer、wms.stockOut。字段权限编码必须稳定,不应随着表名重构频繁变化。
|
业务资源 |
resource_code |
说明 |
|
商品档案 |
pms.goods |
商品主数据、商品选择、商品导出 |
|
客户档案 |
crm.customer |
客户基础信息、账期、授信、余额 |
|
供应商档案 |
sup.supplier |
供应商基础信息、结算账户、应付余额 |
|
销售订单 |
oms.salesOrder |
销售单主表与明细字段 |
|
采购订单 |
pur.purchaseOrder |
采购单主表与明细字段 |
|
入库单 |
wms.stockIn |
采购入库、调拨入库、退货入库 |
|
出库单 |
wms.stockOut |
销售出库、报损出库、调拨出库 |
|
门店库存 |
hwms.inventory |
门店库存、货位、批次 |
|
应收账款 |
fin.receivable |
客户应收、已收、未收、对账 |
|
应付账款 |
fin.payable |
供应商应付、已付、未付、对账 |
建议格式:终端.模块.业务动作.页面位置。例如 admin.sales.order.create.selectGoods。场景编码是字段权限落地的关键,因为同一个资源在不同页面、不同终端、不同动作下字段权限不同。
|
业务场景 |
scene_code |
|
后台商品列表 |
admin.pms.goods.list |
|
后台商品详情 |
admin.pms.goods.detail |
|
销售开单选择商品 |
admin.sales.order.create.selectGoods |
|
销售出库选择商品 |
admin.wms.stockOut.create.selectGoods |
|
商品导出 |
admin.pms.goods.export |
|
客户对账单打印 |
admin.fin.customerStatement.print |
|
客户扫码查看配送单 |
share.customer.delivery.view |
|
供应商扫码查看采购单 |
share.supplier.purchase.view |
|
司机小程序查看配送任务 |
mobile.driver.delivery.task |
field_code 推荐使用 Java VO 字段名,而不是数据库字段名。数据库字段名只作为映射字段 db_column 存储。这样可以降低表结构演进对权限配置的影响。
field_code = costPrice
field_path = detailList[].costPrice
db_table = pms_goods
db_column = cost_price
|
表名 |
用途 |
是否核心 |
|
sys_field_resource |
字段资源表 |
核心 |
|
sys_field_define |
字段定义表 |
核心 |
|
sys_field_scene |
字段场景表 |
核心 |
|
sys_field_scene_field |
场景字段默认策略表 |
核心 |
|
sys_field_policy |
字段授权策略主表 |
核心 |
|
sys_field_policy_item |
字段授权策略明细表 |
核心 |
|
sys_field_mask_rule |
字段脱敏规则表 |
核心 |
|
sys_field_permission_log |
字段权限命中/越权日志表 |
审计 |
|
sys_field_policy_change_log |
字段权限配置变更日志表 |
审计 |
|
sys_field_cache_version |
字段权限缓存版本表 |
性能 |
|
sys_field_api_bind |
接口与资源场景绑定表 |
兼容 |
定义业务资源,例如商品、客户、供应商、销售订单、入库单、出库单。
CREATE TABLE sys_field_resource (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID,0表示系统级资源',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码,如 pms.goods',
resource_name VARCHAR(100) NOT NULL COMMENT '资源名称,如 商品档案',
domain_code VARCHAR(50) NOT NULL COMMENT '领域编码,如 pms/oms/wms/crm/fin',
module_code VARCHAR(50) DEFAULT NULL COMMENT '模块编码',
main_table VARCHAR(100) DEFAULT NULL COMMENT '主表名',
entity_class VARCHAR(255) DEFAULT NULL COMMENT 'Entity类名',
vo_class VARCHAR(255) DEFAULT NULL COMMENT 'VO类名',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_by BIGINT DEFAULT NULL COMMENT '创建人',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by BIGINT DEFAULT NULL COMMENT '更新人',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_tenant_resource (tenant_id, resource_code),
KEY idx_domain_code (domain_code),
KEY idx_module_code (module_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-字段资源表';
定义资源字段,包括字段路径、数据库映射、敏感等级和默认策略。
CREATE TABLE sys_field_define (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID,0表示系统级字段',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
field_code VARCHAR(100) NOT NULL COMMENT '字段编码,如 costPrice',
field_name VARCHAR(100) NOT NULL COMMENT '字段名称,如 成本价',
field_path VARCHAR(200) NOT NULL COMMENT '字段路径,支持 detailList[].costPrice',
db_table VARCHAR(100) DEFAULT NULL COMMENT '数据库表名',
db_table_alias VARCHAR(50) DEFAULT NULL COMMENT 'SQL表别名',
db_column VARCHAR(100) DEFAULT NULL COMMENT '数据库字段名,如 cost_price',
data_type VARCHAR(50) DEFAULT NULL COMMENT '数据类型',
sensitivity_level TINYINT NOT NULL DEFAULT 1 COMMENT '敏感等级:1普通,2内部,3敏感,4高敏,5极高敏',
default_read_mode VARCHAR(20) NOT NULL DEFAULT 'PLAIN' COMMENT '默认读取模式',
default_edit_mode VARCHAR(20) NOT NULL DEFAULT 'EDITABLE' COMMENT '默认编辑模式',
default_query_mode VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '默认查询模式',
default_sort_mode VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '默认排序模式',
default_import_mode VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '默认导入模式',
default_export_mode VARCHAR(20) NOT NULL DEFAULT 'PLAIN' COMMENT '默认导出模式',
default_print_mode VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '默认打印模式',
default_render_mode VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '默认PDF/图片生成模式',
default_share_mode VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '默认分享模式',
mask_rule_code VARCHAR(50) DEFAULT NULL COMMENT '默认脱敏规则编码',
frontend_component VARCHAR(50) DEFAULT NULL COMMENT '前端组件类型',
sort_no INT NOT NULL DEFAULT 0 COMMENT '排序号',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_resource_field (tenant_id, resource_code, field_code),
KEY idx_resource_code (resource_code),
KEY idx_sensitivity_level (sensitivity_level),
KEY idx_db_column (db_table, db_column)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-字段定义表';
定义字段权限适用的业务场景。
CREATE TABLE sys_field_scene (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID,0表示系统级场景',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
scene_code VARCHAR(150) NOT NULL COMMENT '场景编码',
scene_name VARCHAR(100) NOT NULL COMMENT '场景名称',
scene_type VARCHAR(50) NOT NULL COMMENT '场景类型:LIST/DETAIL/FORM/EXPORT/PRINT/SHARE/API',
terminal_type VARCHAR(50) NOT NULL DEFAULT 'ADMIN' COMMENT
'终端:ADMIN/MOBILE/MINIAPP/POS/SHARE',
biz_action VARCHAR(50) DEFAULT NULL COMMENT '业务动作:VIEW/CREATE/UPDATE/APPROVE/EXPORT',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_scene_code (tenant_id, scene_code),
KEY idx_resource_code (resource_code),
KEY idx_scene_type (scene_type),
KEY idx_terminal_type (terminal_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-字段场景表';
定义同一字段在不同场景下的默认权限。
CREATE TABLE sys_field_scene_field (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
scene_code VARCHAR(150) NOT NULL COMMENT '场景编码',
field_code VARCHAR(100) NOT NULL COMMENT '字段编码',
read_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认读取模式',
edit_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认编辑模式',
query_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认查询模式',
sort_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认排序模式',
import_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认导入模式',
export_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认导出模式',
print_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认打印模式',
render_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认渲染模式',
share_mode VARCHAR(20) DEFAULT NULL COMMENT '场景默认分享模式',
visible_flag TINYINT NOT NULL DEFAULT 1 COMMENT '前端是否显示:0否,1是',
readonly_flag TINYINT NOT NULL DEFAULT 0 COMMENT '前端是否只读:0否,1是',
required_flag TINYINT NOT NULL DEFAULT 0 COMMENT '是否必填:0否,1是',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_scene_field (tenant_id, scene_code, field_code),
KEY idx_resource_scene (tenant_id, resource_code, scene_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-场景字段默认策略表';
定义授权主体,如用户、角色、部门、岗位、门店、租户。
CREATE TABLE sys_field_policy (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
policy_code VARCHAR(100) NOT NULL COMMENT '策略编码',
policy_name VARCHAR(100) NOT NULL COMMENT '策略名称',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
scene_code VARCHAR(150) NOT NULL COMMENT '场景编码',
subject_type VARCHAR(30) NOT NULL COMMENT '主体类型:USER/ROLE/DEPT/POST/SHOP/TENANT',
subject_id BIGINT NOT NULL COMMENT '主体ID',
effect VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '效果:ALLOW/DENY',
priority INT NOT NULL DEFAULT 100 COMMENT '优先级,数字越小优先级越高',
effective_start_time DATETIME DEFAULT NULL COMMENT '生效开始时间',
effective_end_time DATETIME DEFAULT NULL COMMENT '生效结束时间',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_by BIGINT DEFAULT NULL COMMENT '创建人',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by BIGINT DEFAULT NULL COMMENT '更新人',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_policy_subject (tenant_id, resource_code, scene_code, subject_type, subject_id),
KEY idx_subject (tenant_id, subject_type, subject_id),
KEY idx_resource_scene (tenant_id, resource_code, scene_code),
KEY idx_status_time (status, effective_start_time, effective_end_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-字段授权策略主表';
定义策略下每个字段的读取、编辑、查询、导出、打印、分享权限。
CREATE TABLE sys_field_policy_item (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
policy_id BIGINT NOT NULL COMMENT '策略主表ID',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
scene_code VARCHAR(150) NOT NULL COMMENT '场景编码',
field_code VARCHAR(100) NOT NULL COMMENT '字段编码',
read_mode VARCHAR(20) DEFAULT NULL COMMENT '读取权限',
edit_mode VARCHAR(20) DEFAULT NULL COMMENT '编辑权限',
query_mode VARCHAR(20) DEFAULT NULL COMMENT '查询权限',
sort_mode VARCHAR(20) DEFAULT NULL COMMENT '排序权限',
import_mode VARCHAR(20) DEFAULT NULL COMMENT '导入权限',
export_mode VARCHAR(20) DEFAULT NULL COMMENT '导出权限',
print_mode VARCHAR(20) DEFAULT NULL COMMENT '打印权限',
render_mode VARCHAR(20) DEFAULT NULL COMMENT 'PDF/图片生成权限',
share_mode VARCHAR(20) DEFAULT NULL COMMENT '分享权限',
mask_rule_code VARCHAR(50) DEFAULT NULL COMMENT '脱敏规则',
condition_json JSON DEFAULT NULL COMMENT '条件表达式',
effect VARCHAR(20) NOT NULL DEFAULT 'ALLOW' COMMENT '效果:ALLOW/DENY',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_policy_field (tenant_id, policy_id, field_code),
KEY idx_resource_scene_field (tenant_id, resource_code, scene_code, field_code),
KEY idx_field_code (field_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-字段授权策略明细表';
统一管理脱敏规则。
CREATE TABLE sys_field_mask_rule (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID,0表示系统级规则',
rule_code VARCHAR(50) NOT NULL COMMENT '规则编码,如 MOBILE/ID_CARD/BANK_CARD',
rule_name VARCHAR(100) NOT NULL COMMENT '规则名称',
rule_type VARCHAR(50) NOT NULL COMMENT '规则类型:MOBILE/EMAIL/ID_CARD/BANK_CARD/ADDRESS/CUSTOM',
rule_config JSON DEFAULT NULL COMMENT '规则配置JSON',
example_before VARCHAR(200) DEFAULT NULL COMMENT '脱敏前示例',
example_after VARCHAR(200) DEFAULT NULL COMMENT '脱敏后示例',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_rule_code (tenant_id, rule_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-脱敏规则表';
记录敏感字段访问、脱敏、隐藏、越权、导出、打印、分享行为。
CREATE TABLE sys_field_permission_log (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
user_id BIGINT NOT NULL COMMENT '操作用户ID',
username VARCHAR(100) DEFAULT NULL COMMENT '用户名',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
scene_code VARCHAR(150) NOT NULL COMMENT '场景编码',
field_code VARCHAR(100) NOT NULL COMMENT '字段编码',
operation_type VARCHAR(50) NOT NULL COMMENT '动作类型:READ/UPDATE/QUERY/EXPORT/PRINT/SHARE',
permission_result VARCHAR(50) NOT NULL COMMENT '权限结果:PLAIN/MASK/HIDE/FORBIDDEN',
request_uri VARCHAR(500) DEFAULT NULL COMMENT '请求URI',
request_method VARCHAR(20) DEFAULT NULL COMMENT '请求方法',
request_param JSON DEFAULT NULL COMMENT '请求参数',
ip VARCHAR(50) DEFAULT NULL COMMENT 'IP地址',
user_agent VARCHAR(500) DEFAULT NULL COMMENT 'User-Agent',
risk_level TINYINT NOT NULL DEFAULT 1 COMMENT '风险等级:1低,2中,3高,4严重',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
KEY idx_user_time (tenant_id, user_id, create_time),
KEY idx_resource_scene (tenant_id, resource_code, scene_code),
KEY idx_field_action (field_code, operation_type),
KEY idx_risk_level (risk_level)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-命中日志表';
记录字段权限配置变更,便于审计和追溯。
CREATE TABLE sys_field_policy_change_log (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
operator_id BIGINT NOT NULL COMMENT '操作人ID',
operator_name VARCHAR(100) DEFAULT NULL COMMENT '操作人名称',
change_type VARCHAR(30) NOT NULL COMMENT 'CREATE/UPDATE/DELETE/ENABLE/DISABLE',
resource_code VARCHAR(100) DEFAULT NULL COMMENT '资源编码',
scene_code VARCHAR(150) DEFAULT NULL COMMENT '场景编码',
policy_id BIGINT DEFAULT NULL COMMENT '策略ID',
before_json JSON DEFAULT NULL COMMENT '变更前JSON',
after_json JSON DEFAULT NULL COMMENT '变更后JSON',
ip VARCHAR(50) DEFAULT NULL COMMENT 'IP地址',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
KEY idx_operator_time (tenant_id, operator_id, create_time),
KEY idx_policy_id (policy_id),
KEY idx_resource_scene (resource_code, scene_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-策略变更日志表';
权限配置变更后用于刷新 Redis 和 Caffeine 缓存。
CREATE TABLE sys_field_cache_version (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
version_no BIGINT NOT NULL DEFAULT 1 COMMENT '当前版本号',
last_refresh_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后刷新时间',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
UNIQUE KEY uk_tenant_id (tenant_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-缓存版本表';
将老接口与 resource_code、scene_code、operation_type 绑定,降低改造成本。
CREATE TABLE sys_field_api_bind (
id BIGINT NOT NULL PRIMARY KEY COMMENT '主键ID',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID,0表示系统级绑定',
request_uri VARCHAR(500) NOT NULL COMMENT '接口URI,支持精确匹配或Ant模式',
request_method VARCHAR(20) NOT NULL COMMENT 'GET/POST/PUT/DELETE',
resource_code VARCHAR(100) NOT NULL COMMENT '资源编码',
scene_code VARCHAR(150) NOT NULL COMMENT '场景编码',
operation_type VARCHAR(50) NOT NULL COMMENT 'READ/CREATE/UPDATE/QUERY/EXPORT/PRINT/SHARE',
match_type VARCHAR(20) NOT NULL DEFAULT 'EXACT' COMMENT 'EXACT/ANT/REGEX',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0停用,1启用',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否,1是',
UNIQUE KEY uk_api_bind (tenant_id, request_uri, request_method, operation_type),
KEY idx_resource_scene (tenant_id, resource_code, scene_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一字段权限-接口资源场景绑定表';
com.sxb.system.fieldpermission
├── annotation
│ ├── SxbFieldPermission.java
│ ├── SxbFieldWriteCheck.java
│ └── SxbFieldExportCheck.java
├── api
│ ├── FieldResourceController.java
│ ├── FieldDefineController.java
│ ├── FieldSceneController.java
│ ├── FieldPolicyController.java
│ └── FieldSchemaController.java
├── application
│ ├── FieldPermissionAppService.java
│ ├── FieldPolicyAppService.java
│ ├── FieldSchemaAppService.java
│ └── FieldAuditAppService.java
├── domain
│ ├── engine
│ │ ├── FieldPermissionEngine.java
│ │ ├── FieldPolicyMatchEngine.java
│ │ ├── FieldPolicyMergeEngine.java
│ │ ├── FieldMaskEngine.java
│ │ └── FieldPermissionCacheService.java
│ ├── model
│ │ ├── FieldPermissionContext.java
│ │ ├── FieldPermissionSnapshot.java
│ │ ├── FieldPermissionItem.java
│ │ └── FieldMaskResult.java
│ └── enums
│ ├── FieldOperationTypeEnum.java
│ ├── FieldReadModeEnum.java
│ ├── FieldEditModeEnum.java
│ ├── FieldQueryModeEnum.java
│ ├── FieldExportModeEnum.java
│ └── FieldSensitivityLevelEnum.java
├── infrastructure
│ ├── entity
│ ├── mapper
│ ├── repository
│ └── cache
└── support
├── FieldPermissionAspect.java
├── FieldPermissionResponseAdvice.java
├── FieldWriteCheckAspect.java
├── FieldQueryGuard.java
├── FieldExportGuard.java
├── FieldPrintGuard.java
└── FieldShareGuard.java
public interface FieldPermissionEngine {
FieldPermissionSnapshot calculate(FieldPermissionContext context);
<T> T filterReadResult(T data, FieldPermissionContext context);
<T> List<T> filterReadList(List<T> dataList, FieldPermissionContext context);
void checkCreateFields(Object dto, FieldPermissionContext context);
void checkUpdateFields(Object dto, FieldPermissionContext context);
void checkQueryFields(QueryRequest queryRequest, FieldPermissionContext context);
void checkSortFields(QueryRequest queryRequest, FieldPermissionContext context);
List<ExportField> filterExportFields(List<ExportField> fields, FieldPermissionContext
context);
Map<String, Object> filterPrintData(Map<String, Object> data, FieldPermissionContext
context);
Map<String, Object> filterShareData(Map<String, Object> data, FieldPermissionContext
context);
}
@Data
public class FieldPermissionContext {
private Long tenantId;
private Long userId;
private String username;
private List<Long> roleIds;
private List<Long> deptIds;
private List<Long> postIds;
private Long shopId;
private String terminalType;
private String resourceCode;
private String sceneCode;
private FieldOperationTypeEnum operationType;
private String requestUri;
private String requestMethod;
private Boolean superAdmin;
private Map<String, Object> bizContext;
}
@Data
public class FieldPermissionSnapshot {
private Long tenantId;
private Long userId;
private String resourceCode;
private String sceneCode;
private String operationType;
private Long versionNo;
private Map<String, FieldPermissionItem> fieldMap;
}
@Data
public class FieldPermissionItem {
private String fieldCode;
private String fieldName;
private String fieldPath;
private Integer sensitivityLevel;
private String readMode;
private String editMode;
private String queryMode;
private String sortMode;
private String importMode;
private String exportMode;
private String printMode;
private String renderMode;
private String shareMode;
private String maskRuleCode;
private Boolean visible;
private Boolean editable;
private Boolean queryable;
private Boolean exportable;
private Boolean printable;
private Boolean shareable;
}
1. 构建 FieldPermissionContext,包括租户、用户、角色、部门、岗位、门店、终端、资源、场景、操作类型。
2. 判断超级管理员,但超级管理员是否默认拥有高敏字段应由系统参数控制。
3. 加载字段定义 sys_field_define。
4. 加载场景默认策略 sys_field_scene_field。
5. 加载命中的用户、岗位、角色、部门、门店、租户默认策略。
6. 按优先级合并策略,显式 DENY 永远优先。
7. 应用敏感等级默认规则。
8. 生成 FieldPermissionSnapshot 并写入缓存。
9. 根据操作类型执行返回过滤、编辑校验、查询校验、导出过滤、打印过滤或分享过滤。
|
规则 |
说明 |
|
显式拒绝优先 |
只要命中 DENY,就不能通过其他角色绕过。 |
|
用户优先 |
用户级策略优先于岗位、角色、部门、租户策略。 |
|
岗位优先于部门 |
岗位通常比部门更能表示业务职责。 |
|
高敏字段从严 |
多角色命中高敏字段时取更严格权限。 |
|
普通字段可从宽 |
普通字段多角色命中可取更宽松权限。 |
|
场景优先 |
场景默认策略优先于字段默认策略。 |
|
导出/分享更严格 |
页面能看不代表可以导出或分享。 |
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SxbFieldPermission {
String resourceCode();
String sceneCode();
FieldOperationTypeEnum operationType() default FieldOperationTypeEnum.READ;
}
@SxbFieldPermission(
resourceCode = "pms.goods",
sceneCode = "admin.sales.order.create.selectGoods",
operationType = FieldOperationTypeEnum.READ
)
@GetMapping("/goods/select")
public List<GoodsSelectVO> selectGoods(GoodsSelectQueryDTO queryDTO) {
return goodsService.selectGoods(queryDTO);
}
对于老接口,不建议一次性大面积修改 Controller 方法签名。可以先将接口路径与资源场景写入 sys_field_api_bind。运行时 AOP 根据请求路径匹配 resource_code、scene_code、operation_type。
|
接口 |
资源 |
场景 |
动作 |
|
/api/pms/goods/page |
pms.goods |
admin.pms.goods.list |
READ |
|
/api/pms/goods/detail |
pms.goods |
admin.pms.goods.detail |
READ |
|
/api/oms/order/goods/select |
pms.goods |
admin.sales.order.create.selectGoods |
READ |
|
/api/crm/customer/update |
crm.customer |
admin.crm.customer.update |
UPDATE |
推荐第一版采用 ResponseBodyAdvice + AOP 的方式,在 Controller 返回后统一过滤 VO、List、Page、IPage、Map、嵌套对象、明细列表。不要第一版就全局 SQL 拦截,避免误伤复杂 Mapper。
Controller 返回 VO / List / Page
↓
ResponseBodyAdvice 拦截
↓
读取 SxbFieldPermission 或 sys_field_api_bind
↓
构建 FieldPermissionContext
↓
FieldPermissionEngine.calculate()
↓
对字段执行 PLAIN / MASK / HIDE / FORBIDDEN
↓
返回前端安全结果
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SxbFieldWriteCheck {
String resourceCode();
String sceneCode();
FieldOperationTypeEnum operationType();
}
@SxbFieldWriteCheck(
resourceCode = "crm.customer",
sceneCode = "admin.crm.customer.update",
operationType = FieldOperationTypeEnum.UPDATE
)
@PostMapping("/update")
public Boolean update(@RequestBody CustomerUpdateDTO dto) {
return customerService.update(dto);
}
编辑校验关键点
修改接口不能只判断 DTO 中是否存在字段,还要对比数据库原值。无权限字段如果没有发生变更可放行,如果发生变更必须拒绝。
升鲜宝高级查询组件、排序组件、动态列组件必须统一通过 QueryFacade 校验字段权限。禁止业务模块直接拼接用户提交的字段名。
前端提交查询条件
↓
QueryFacade 解析 queryFields / sortFields / selectFields
↓
FieldPermissionEngine.checkQueryFields()
↓
FieldPermissionEngine.checkSortFields()
↓
无权限字段直接拒绝
↓
构建安全 SQL
导出必须使用 EXPORT 权限,不能复用页面 READ 权限。导出任务中心必须记录导出字段清单、筛选条件、导出数量、是否包含敏感字段、文件地址和操作人。
|
export_mode |
处理方式 |
|
PLAIN |
原值导出 |
|
MASK |
脱敏导出 |
|
FORBIDDEN |
不允许导出 |
|
APPROVAL_REQUIRED |
进入审批流程,审批通过后导出 |
打印模板字段不能直接读取业务 VO,必须经过 PRINT / RENDER 权限过滤。客户配送单、对账单、采购单、小票图片、单据 PDF 都需要使用独立的输出场景。
分享码访问不能继承当前员工权限,必须构建独立的 SHARE 权限上下文。外部客户、供应商、司机使用不同 share scene_code,默认最小字段集。
FieldPermissionContext context = new FieldPermissionContext();
context.setTenantId(tenantId);
context.setUserId(0L);
context.setTerminalType("SHARE");
context.setResourceCode("oms.deliveryBill");
context.setSceneCode("share.customer.delivery.view");
context.setOperationType(FieldOperationTypeEnum.SHARE);
GET /api/system/field-permission/schema
Request:
{
"resourceCode": "pms.goods",
"sceneCode": "admin.sales.order.create.selectGoods"
}
Response:
{
"resourceCode": "pms.goods",
"sceneCode": "admin.sales.order.create.selectGoods",
"versionNo": 12,
"fields": [
{
"fieldCode": "goodsName",
"fieldName": "商品名称",
"visible": true,
"editable": false,
"queryable": true,
"sortable": true,
"exportable": true,
"printable": true,
"shareable": true,
"mask": false,
"component": "input",
"sortNo": 10
},
{
"fieldCode": "costPrice",
"fieldName": "成本价",
"visible": false,
"editable": false,
"queryable": false,
"sortable": false,
"exportable": false,
"printable": false,
"shareable": false,
"mask": false,
"component": "amount",
"sortNo": 90
}
]
}
useFieldPermission(resourceCode, sceneCode)
返回能力:
{
fieldMap,
canVisible,
canEdit,
canQuery,
canExport,
canPrint,
filterColumns,
filterFormItems,
filterQueryItems,
filterExportFields
}
|
前端组件 |
接入方式 |
|
通用表格 |
visibleColumns = fieldPermission.filterColumns(allColumns) |
|
通用表单 |
formItems = fieldPermission.filterFormItems(allFormItems) |
|
字段只读 |
disabled = !fieldPermission.canEdit("creditLimit") |
|
高级查询 |
queryFields = fieldPermission.filterQueryItems(allQueryFields) |
|
导出弹窗 |
exportFields = fieldPermission.filterExportFields(allExportFields) |
|
打印模板设计器 |
只允许拖入 printable=true 的字段 |
系统设置
└── 权限中心
├── 菜单权限
├── 按钮权限
├── 数据权限
├── 字段权限
│ ├── 字段资源管理
│ ├── 字段定义管理
│ ├── 字段场景管理
│ ├── 字段授权管理
│ └── 脱敏规则管理
└── 权限审计日志
|
页面 |
核心功能 |
|
字段资源管理 |
维护 resource_code、资源名称、领域、模块、主表、VO 类、状态。 |
|
字段定义管理 |
维护 field_code、field_path、db_column、敏感等级、默认权限、脱敏规则、前端组件。 |
|
字段场景管理 |
维护 scene_code、终端、场景类型、业务动作、场景说明。 |
|
字段授权管理 |
选择资源、场景、主体,配置读取/编辑/查询/排序/导入/导出/打印/分享权限。 |
|
脱敏规则管理 |
配置手机号、邮箱、身份证、银行卡、地址、金额区间、自定义脱敏。 |
|
审计日志 |
查询敏感字段访问、越权行为、导出行为、配置变更。 |
|
字段 |
读取 |
编辑 |
查询 |
排序 |
导出 |
打印 |
分享 |
脱敏 |
|
商品名称 |
原值 |
只读 |
允许 |
允许 |
原值 |
允许 |
允许 |
无 |
|
手机号 |
脱敏 |
只读 |
禁止 |
禁止 |
脱敏 |
脱敏 |
脱敏 |
MOBILE |
|
成本价 |
隐藏 |
禁止 |
禁止 |
禁止 |
禁止 |
禁止 |
禁止 |
无 |
|
毛利率 |
隐藏 |
禁止 |
禁止 |
禁止 |
审批 |
禁止 |
禁止 |
无 |
|
规则编码 |
规则说明 |
脱敏示例 |
|
MOBILE |
手机号 |
138****5678 |
|
|
邮箱 |
a***@xx.com |
|
ID_CARD |
身份证 |
3301**********1234 |
|
BANK_CARD |
银行卡 |
**** **** **** 1234 |
|
ADDRESS |
地址 |
浙江省杭州市*** |
|
AMOUNT_RANGE |
金额区间 |
1万-5万 |
|
CUSTOM |
自定义规则 |
按 rule_config 执行 |
脱敏原则
脱敏不是简单字符串替换。必须根据字段类型、字段长度、终端类型、场景类型决定脱敏策略。例如司机可临时拨打客户电话,但页面仍可显示脱敏号码;财务可查看完整客户手机号,但导出时仍可配置脱敏。
字段权限属于高频读取配置,必须使用 Caffeine 本地缓存 + Redis 分布式缓存 + 版本号刷新。
|
缓存对象 |
Key 示例 |
|
字段权限快照 |
sxb:field:permission:{tenantId}:{userId}:{resourceCode}:{sceneCode}:{operationType}:{versionNo} |
|
字段定义 |
sxb:field:define:{tenantId}:{resourceCode}:{versionNo} |
|
场景字段 |
sxb:field:scene:{tenantId}:{sceneCode}:{versionNo} |
|
主体策略 |
sxb:field:policy:{tenantId}:{subjectType}:{subjectId}:{versionNo} |
管理员修改字段权限
↓
保存 sys_field_policy / sys_field_policy_item
↓
写入 sys_field_policy_change_log
↓
sys_field_cache_version.version_no + 1
↓
发布 Redis Topic
↓
各服务清理 Caffeine 本地缓存
↓
下一次请求重新计算权限
|
行为 |
是否记录 |
风险建议 |
|
高敏字段被查看 |
必须 |
高 |
|
字段被脱敏 |
可采样 |
低/中 |
|
字段被隐藏 |
可采样 |
低 |
|
无权限字段查询 |
必须 |
高 |
|
无权限字段排序 |
必须 |
高 |
|
无权限字段编辑 |
必须 |
高 |
|
无权限字段导出 |
必须 |
严重 |
|
敏感字段批量导出 |
必须 |
严重 |
|
打印包含敏感字段 |
必须 |
高 |
|
分享码访问敏感字段 |
必须 |
严重 |
|
字段编码 |
字段名称 |
敏感等级 |
默认策略 |
|
goodsName |
商品名称 |
PUBLIC |
可见 |
|
goodsCode |
商品编码 |
PUBLIC |
可见 |
|
salePrice |
销售价 |
INTERNAL |
销售相关场景可见 |
|
purchasePrice |
采购价 |
SECRET |
默认隐藏 |
|
costPrice |
成本价 |
SECRET |
默认隐藏 |
|
grossProfitRate |
毛利率 |
SECRET |
默认隐藏 |
|
inventoryQty |
库存数量 |
INTERNAL |
按场景可见 |
|
inventoryAmount |
库存金额 |
SECRET |
默认隐藏 |
|
internalRemark |
内部备注 |
SENSITIVE |
默认隐藏 |
|
字段编码 |
字段名称 |
敏感等级 |
默认策略 |
|
customerName |
客户名称 |
PUBLIC |
可见 |
|
contactName |
联系人 |
INTERNAL |
可见 |
|
contactPhone |
手机号 |
SENSITIVE |
默认脱敏 |
|
address |
地址 |
SENSITIVE |
按数据权限 |
|
creditLimit |
授信额度 |
SECRET |
财务/老板可见 |
|
accountPeriodDays |
账期 |
SECRET |
财务/老板可见 |
|
receivableBalance |
应收余额 |
SECRET |
财务/老板可见 |
|
internalRemark |
内部备注 |
SENSITIVE |
默认隐藏 |
|
字段编码 |
字段名称 |
敏感等级 |
默认策略 |
|
supplierName |
供应商名称 |
PUBLIC |
可见 |
|
contactName |
联系人 |
INTERNAL |
可见 |
|
contactPhone |
手机号 |
SENSITIVE |
默认脱敏 |
|
bankAccount |
银行账户 |
CRITICAL |
默认禁止 |
|
bankName |
开户行 |
SECRET |
财务可见 |
|
payableBalance |
应付余额 |
SECRET |
财务/老板可见 |
|
internalRating |
内部评价 |
SENSITIVE |
采购主管可见 |
|
字段编码 |
字段名称 |
敏感等级 |
默认策略 |
|
orderNo |
订单编号 |
PUBLIC |
可见 |
|
customerName |
客户名称 |
PUBLIC |
可见 |
|
goodsName |
商品名称 |
PUBLIC |
可见 |
|
qty |
数量 |
PUBLIC |
可见 |
|
salePrice |
销售单价 |
INTERNAL |
销售/财务可见 |
|
amount |
金额 |
INTERNAL |
销售/财务可见 |
|
costPrice |
成本价 |
SECRET |
默认隐藏 |
|
grossProfitRate |
毛利率 |
SECRET |
默认隐藏 |
|
internalRemark |
内部备注 |
SENSITIVE |
默认隐藏 |
|
字段编码 |
字段名称 |
敏感等级 |
默认策略 |
|
warehouseName |
仓库 |
PUBLIC |
可见 |
|
locationCode |
库位 |
INTERNAL |
仓管可见 |
|
batchNo |
批次 |
INTERNAL |
仓管可见 |
|
productionDate |
生产日期 |
INTERNAL |
仓管可见 |
|
inventoryQty |
库存数量 |
INTERNAL |
仓管可见 |
|
inventoryAmount |
库存金额 |
SECRET |
财务/老板可见 |
|
costPrice |
成本价 |
SECRET |
默认隐藏 |
|
字段编码 |
字段名称 |
敏感等级 |
默认策略 |
|
receivableAmount |
应收金额 |
SECRET |
财务/老板可见 |
|
receivedAmount |
已收金额 |
SECRET |
财务/老板可见 |
|
unpaidAmount |
未收金额 |
SECRET |
财务/老板可见 |
|
payableAmount |
应付金额 |
SECRET |
财务/老板可见 |
|
bankAccount |
银行账户 |
CRITICAL |
财务主管可见 |
|
voucherRemark |
凭证备注 |
SENSITIVE |
财务可见 |
|
角色 |
建议权限 |
|
老板 |
大部分业务字段可见;成本、毛利、余额可见;敏感导出必须记录日志;银行账号、身份证等建议二次验证或审批。 |
|
财务 |
应收、应付、账期、授信、余额、成本金额可见;导出敏感字段需要审计。 |
|
采购 |
采购价、供应商联系人可见;供应商结算账户默认不可见;客户财务信息不可见。 |
|
销售 |
销售价、客户基础信息可见;手机号默认脱敏或按客户归属显示;成本价、毛利率不可见。 |
|
仓管 |
库存数量、库位、批次、生产日期可见;价格类字段和库存金额默认不可见。 |
|
司机 |
仅配送任务相关字段可见;客户地址可见;客户电话可脱敏或临时拨打;金额、成本、毛利不可见。 |
|
门店店员 |
门店商品、库存、销售价可见;成本价不可见;客户隐私脱敏。 |
图 3 一次性底座重构 + 分批接入路线
|
阶段 |
交付内容 |
验收标准 |
|
第一阶段:统一底座 |
11 张核心表、fieldpermission 模块、引擎、上下文、策略合并、脱敏、缓存、审计、Schema API、基础后台页面。 |
字段权限底座可独立运行。 |
|
第二阶段:公共组件 |
通用表格、表单、高级查询、导入导出、打印、PDF/图片、分享码组件接入。 |
公共组件可根据 Schema 自动控制字段。 |
|
第三阶段:核心模块 |
商品、客户、供应商、销售订单、采购订单、WMS、财务接入。 |
核心敏感字段不再泄露。 |
|
第四阶段:历史接口兼容 |
通过 sys_field_api_bind 绑定老接口,减少 Controller 改造。 |
老接口也能执行字段过滤。 |
|
第五阶段:灰度上线 |
逐步开启 read-filter、write-check、query-check、export-check、print-check、share-check。 |
可按租户/模块/场景灰度。 |
|
第六阶段:审计优化 |
敏感访问审计、越权告警、导出审批、性能优化、SQL 字段裁剪。 |
安全闭环形成。 |
field.permission.enabled=true
field.permission.schema.enabled=true
field.permission.read-filter.enabled=false
field.permission.write-check.enabled=false
field.permission.query-check.enabled=false
field.permission.export-check.enabled=false
field.permission.print-check.enabled=false
field.permission.share-check.enabled=false
推荐上线顺序:先开启 Schema,再开启前端隐藏,再开启后端返回过滤,再开启查询校验、编辑校验,最后开启导出、打印、分享强校验。
|
验收项 |
标准 |
|
字段资源 |
所有核心模块已注册 resource_code。 |
|
字段定义 |
核心 VO 字段已入库,敏感等级准确。 |
|
场景定义 |
列表、详情、表单、导出、打印、分享均有 scene_code。 |
|
返回过滤 |
无权限字段不会返回原值。 |
|
编辑校验 |
无权限字段不能被修改。 |
|
查询校验 |
无权限字段不能作为查询条件。 |
|
排序校验 |
无权限字段不能排序。 |
|
导出过滤 |
无权限字段不能导出,敏感字段导出有日志或审批。 |
|
打印过滤 |
无权限字段不能打印或渲染到 PDF/图片。 |
|
分享过滤 |
外部分享不泄露内部字段。 |
|
缓存刷新 |
权限配置修改后能实时生效。 |
|
测试用例 |
预期结果 |
|
普通销售员查看商品成本价 |
看不到,返回 null 或字段不存在。 |
|
普通销售员抓包请求成本价 |
后端仍然不返回原值。 |
|
普通销售员使用成本价查询 |
后端拒绝并记录越权日志。 |
|
普通销售员修改客户授信额度 |
后端拒绝。 |
|
仓管导出商品库存金额 |
后端拒绝或走审批。 |
|
客户扫码查看配送单 |
不出现成本、毛利、内部备注。 |
|
司机查看配送任务 |
只看到配送所需信息。 |
|
财务导出客户余额 |
允许但记录敏感导出日志。 |
|
不要第一版直接全局 SQL 拦截 MyBatis-Plus InnerInterceptor 可作为后期优化。第一版建议采用 VO 返回过滤 + DTO 写入校验 + QueryFacade 字段校验,风险最低、落地最快。 |
|
|
阶段 |
策略 |
|
第一版 |
VO 过滤 + DTO 校验 + QueryFacade 查询/排序校验。 |
|
第二版 |
QueryFacade SELECT 字段裁剪,减少无权限字段查询。 |
|
第三版 |
MyBatis-Plus InnerInterceptor 增强,多表 join、别名、子查询逐步支持。 |
|
长期演进 |
字段级审批、字段级水印、字段访问画像、异常导出告警、字段级数据血缘。 |
-- 脱敏规则初始化
INSERT INTO sys_field_mask_rule
(id, tenant_id, rule_code, rule_name, rule_type, rule_config, example_before, example_after,
status)
VALUES
(1, 0, 'MOBILE', '手机号脱敏', 'MOBILE', JSON_OBJECT('prefix',3,'suffix',4), '13812345678',
'138****5678', 1),
(2, 0, 'EMAIL', '邮箱脱敏', 'EMAIL', JSON_OBJECT('prefix',1), 'abc@test.com', 'a***@test.com', 1),
(3, 0, 'ID_CARD', '身份证脱敏', 'ID_CARD', JSON_OBJECT('prefix',4,'suffix',4), '330102199001011234',
'3301**********1234', 1),
(4, 0, 'BANK_CARD', '银行卡脱敏', 'BANK_CARD', JSON_OBJECT('suffix',4), '6222021234567890123', '****
**** **** 0123', 1);
-- 商品资源初始化
INSERT INTO sys_field_resource
(id, tenant_id, resource_code, resource_name, domain_code, module_code, main_table, vo_class,
status)
VALUES
(1001, 0, 'pms.goods', '商品档案', 'pms', 'goods', 'pms_goods', 'com.sxb.pms.vo.GoodsVO', 1);
-- 商品高敏字段示例
INSERT INTO sys_field_define
(id, tenant_id, resource_code, field_code, field_name, field_path, db_table, db_column,
data_type, sensitivity_level, default_read_mode, default_export_mode, default_share_mode,
status)
VALUES
(1101, 0, 'pms.goods', 'costPrice', '成本价', 'costPrice', 'pms_goods', 'cost_price',
'decimal', 4, 'HIDE', 'FORBIDDEN', 'FORBIDDEN', 1),
(1102, 0, 'pms.goods', 'grossProfitRate', '毛利率', 'grossProfitRate', 'pms_goods',
'gross_profit_rate',
'decimal', 4, 'HIDE', 'FORBIDDEN', 'FORBIDDEN', 1);
|
接口 |
方法 |
说明 |
|
/api/system/field-resource/page |
GET |
字段资源分页 |
|
/api/system/field-resource/save |
POST |
新增/修改字段资源 |
|
/api/system/field-define/page |
GET |
字段定义分页 |
|
/api/system/field-define/import-from-vo |
POST |
从 VO 扫描导入字段定义 |
|
/api/system/field-scene/page |
GET |
字段场景分页 |
|
/api/system/field-policy/save |
POST |
保存字段授权策略 |
|
/api/system/field-permission/schema |
GET |
获取当前用户字段 Schema |
|
/api/system/field-mask-rule/page |
GET |
脱敏规则分页 |
|
/api/system/field-audit/page |
GET |
字段权限日志查询 |
|
/api/system/field-cache/refresh |
POST |
刷新字段权限缓存版本 |
最终推荐方案
升鲜宝统一字段权限应作为系统级基础能力建设,而不是单个模块的补丁。最终落地形态是:字段资源中心 + 字段定义中心 + 业务场景中心 + 授权策略中心 + 脱敏中心 + 字段权限引擎 + 前端 Schema + 后端全链路强校验 + 审计告警。
按本文档方案落地后,升鲜宝可以在不反复推翻业务代码的前提下,逐步将商品、客户、供应商、订单、采购、WMS、财务、POS、小程序、打印、分享码等能力纳入统一字段安全底座。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。