“多租户(tenant)+ 部门数据权限(dept)双层隔离” 的升鲜宝最终设计(单体 Spring Boot 直接可落地,未来拆微服务也不改核心规则)。

“多租户(tenant)+ 部门数据权限(dept)双层隔离”升鲜宝最终设计(单体 Spring Boot 直接可落地,未来拆微服务也不改核心规则)。

 

1)双层隔离的核心目标

第一层:租户隔离(硬隔离)

  • 所有业务表必须有 tenant_id

  • 所有查询/写入都必须自动带 tenant_id

  • 任何绕过都视为严重漏洞

第二层:组织数据权限(软隔离)

同一租户内,不同角色只能看到自己范围:

  • 部门(dept_id)

  • 门店(shop_id)

  • 仓库(warehouse_id)

  • 以及“本人数据(created_by)”等

最终效果:
tenant_id 把公司隔开;dept/shop/warehouse 把内部组织隔开。


2)统一字段规范(全库强制)

所有业务表(必须)

  • tenant_id BIGINT NOT NULL

  • created_by BIGINT

  • created_at BIGINT

  • deleted TINYINT DEFAULT 0

  • enabled TINYINT DEFAULT 1

需要组织隔离的业务表(按域选择其一或多)

  • dept_id BIGINT(组织/部门维度)

  • shop_id BIGINT(门店域/hwms/pos/销售)

  • warehouse_id BIGINT(公司仓库/wms/成本)

  • company_id BIGINT(主组织维度,可选)

升鲜宝建议:

  • WMS & cost:warehouse_id 必须有

  • HWMS & hcost:shop_id 必须有

  • 订单:sales 走 shop_id,purchase 走 company/warehouse(看你履约)


3)权限模型:RBAC + DataScope(数据范围)

3.1 角色数据范围枚举(DataScopeType)

  • ALL:本租户全量(仅租户管理员/老板)

  • DEPT:本部门

  • DEPT_AND_SUB:本部门及下级

  • SHOPS:指定门店集合

  • WAREHOUSES:指定仓库集合

  • SELF:本人创建的数据

  • CUSTOM:自定义(部门+门店+仓库混合)

注意:所有 scope 都是在 tenant_id 已过滤后 才生效。


4)数据库表结构(权限域)

4.1 组织结构(树)

  • org_dept(id, tenant_id, parent_id, dept_name, path, level, ...)

  • org_shop(id, tenant_id, company_id, ...)

  • org_warehouse(id, tenant_id, company_id, ...)

4.2 用户/角色/权限

  • sys_user(id, tenant_id, username, password_hash, dept_id, is_tenant_admin, ...)

  • sys_role(id, tenant_id, role_code, role_name, ...)

  • sys_user_role(id, tenant_id, user_id, role_id)

  • sys_permission(id, tenant_id, perm_code, perm_name, ...)

  • sys_role_permission(id, tenant_id, role_id, perm_id)

4.3 数据范围(关键)

角色的数据范围策略表:

  • sys_role_data_scope(id, tenant_id, role_id, scope_type, ...)

当 scope_type = SHOPS / WAREHOUSES / CUSTOM 时,需要映射表:

  • sys_role_scope_shop(id, tenant_id, role_id, shop_id)

  • sys_role_scope_warehouse(id, tenant_id, role_id, warehouse_id)

  • sys_role_scope_dept(id, tenant_id, role_id, dept_id)(用于 CUSTOM 或 DEPT 子集)

为什么用“角色”而不是“用户”存数据范围?
因为同角色一批人可复用规则;用户特殊情况可加 sys_user_data_scope_override 做覆盖(可选)。


5)执行链路:必须“双拦截”

5.1 Tenant 拦截(强制)

实现方式(推荐 MyBatis-Plus):

  • TenantLineInnerInterceptor 自动拼:tenant_id = ?

  • 对于不含 tenant_id 的表(平台表 tenant/tenant_module),加入忽略名单

5.2 DataScope 拦截(组织范围)

实现方式(推荐两种之一):

方案 A(推荐):MyBatis-Plus DataPermissionInterceptor

  • 对指定 Mapper 方法开启数据权限

  • 自动拼接:dept_id/shop_id/warehouse_id/created_by 条件

方案 B:AOP + SQL 注入(不推荐长期用)

  • 用 AOP 包一层拼条件,侵入性更高

升鲜宝建议:
Tenant 用 TenantLineInnerInterceptor
数据权限用 DataPermissionInterceptor(可维护、可统一管理)。


6)数据权限规则生成器(核心算法)

对每次请求,先得到一个 DataScopeContext

  • tenantId(必有)

  • userId

  • deptId

  • roleScopes(可能多个角色,取并集 or 取最宽策略)

  • allowedShopIds / allowedWarehouseIds / allowedDeptIds(集合)

然后按业务表的“组织维度”生成 SQL 条件:

示例 1:WMS 库存表(warehouse_id)

 
tenant_id = :tenantId AND warehouse_id IN (:allowedWarehouseIds)

示例 2:HWMS 门店库存表(shop_id)

 
tenant_id = :tenantId AND shop_id IN (:allowedShopIds)

示例 3:通用单据(dept_id + created_by)

若 scope=DEPT_AND_SUB:

 
tenant_id = :tenantId AND dept_id IN (:deptAndChildrenIds)

若 scope=SELF:

 
tenant_id = :tenantId AND created_by = :userId

多角色合并规则(建议)

  • 取并集(更符合“多个角色赋权叠加”)

  • 但租户管理员直接 ALL


7)“表级策略”配置(避免每张表手写)

做一张元数据表:sys_data_scope_rule

  • table_name

  • scope_dimensionDEPT/SHOP/WAREHOUSE/SELF/MIXED

  • dept_field / shop_field / warehouse_field / owner_field

  • enabled

这样 DataPermissionInterceptor 能根据 “表名 → 维度字段” 自动拼接,不需要每个 Mapper 单独写。


8)典型升鲜宝域的推荐隔离维度

推荐维度字段说明
product 商品域 dept_id 或 company_id(可选) 多数情况下租户内共享即可
supplier 供应商域 company_id 或 dept_id 采购组织隔离
wms 公司仓库域 warehouse_id(必须) 仓库是天然数据边界
cost 公司成本域 warehouse_id(必须) 成本必须跟仓库隔离
hwms 门店域 shop_id(必须) 门店是天然边界
hcost 门店成本域 shop_id(必须) 同上
sales 销售域 shop_id + created_by 门店业务
finance 财务域 company_id/ dept_id 视财务组织而定

9)安全底线(必须做)

    1. 所有写入必须自动填充 tenant_id

    2. 所有查询必须自动带 tenant_id(不允许手写遗漏)

    3. 数据权限条件必须在 SQL 层生效(只靠前端/业务逻辑不算)

    4. 对管理员账号(tenant_admin)走白名单放行,但仍必须 tenant 隔离

    5. 审计表(sys_audit_log)也要带 tenant_id + operator_id

posted @ 2026-02-13 14:12  升鲜宝供应链管理系统  阅读(2)  评论(0)    收藏  举报