企业级权限系统设计:数据权限控制的深度解析
data_scopedata_scope值含义适用场景SQL条件示例(简化)SELF仅本人数据个人数据(如自己的报销单)DEPT本部门数据部门内数据(如本部门的订单)本部门及下属部门数据管理层查看下属数据ORG本机构数据(如子公司)跨部门但同机构的数据ALL所有数据管理员权限无额外条件(或1=1指定部门数据(自定义范围)区域负责人查看指定区域数据数据权限控制是企业级系统数据安全的核心防线,其设计需
在企业级应用中,权限系统的核心目标是“让合适的人在合适的场景下访问合适的数据”。功能权限(“能做什么操作”)解决了访问的“动作边界”,而数据权限(“能操作哪些数据”)则定义了访问的“范围边界”。本文将从数据权限的核心构成出发,系统解析四种实现方案的设计细节、技术落地与适用场景,帮助架构师根据业务复杂度选择最优方案。
一、数据权限的核心构成:行级与列级的双重防护
数据权限通过“行级控制”和“列级控制”的组合,实现对数据访问的精细化管理。两者相互补充,共同构建数据安全的“双重防线”。
1. 行级权限:控制“哪些记录可见”
行级权限决定用户能访问数据集合中的哪些记录,核心是“数据范围的过滤”。
- 典型场景:
- 销售只能查看自己跟进的客户(
user_id = 当前用户ID); - 部门经理能查看本部门所有员工的报销单(
dept_id = 当前部门ID); - 区域总监可查看华北、华东地区的订单(
region IN ('华北','华东')); - 财务总监能查看所有金额>100万的合同(
amount > 1000000)。
- 销售只能查看自己跟进的客户(
- 技术本质:通过动态拼接过滤条件(如SQL的
WHERE子句),限制返回的记录集。
2. 列级权限:控制“哪些字段可见”
列级权限决定用户能查看记录中的哪些字段,核心是“数据内容的脱敏与隐藏”。
- 典型场景:
- 普通员工查看客户信息时,身份证号显示为
110****5678(部分隐藏); - 人力资源专员无法查看员工的薪资字段(
salary字段不返回); - 客服只能看到客户的手机号前3位和后4位(
138****5678)。
- 普通员工查看客户信息时,身份证号显示为
- 技术本质:通过动态调整查询的字段列表(如SQL的
SELECT子句),或对返回字段进行脱敏处理。
3. 行级与列级的协同:数据权限的完整闭环
实际业务中,行级与列级权限通常结合使用。例如:
- 规则:“销售经理能查看本部门所有客户的姓名、电话,但不能查看客户的身份证号和家庭住址”。
- 实现:
- 行级:
dept_id = 当前部门ID(过滤本部门客户); - 列级:只返回
name、phone字段,id_card、address字段隐藏。
- 行级:
二、数据权限实现方案:从简单到复杂的演进
数据权限的实现方案随业务复杂度提升而演进,从“硬编码”到“ABAC模型”,灵活性与技术复杂度成正比。选择方案时需权衡“业务需求”“维护成本”与“性能开销”。
方案一:硬编码——简单场景的快速落地
硬编码是最直接的实现方式,通过在代码或SQL中写死数据过滤规则,适用于规则固定、变更极少的场景。
1. 技术实现
-
SQL层面:在查询语句中直接嵌入权限条件。
-- 示例1:销售查看自己的订单 SELECT * FROM orders WHERE sales_id = #{currentUserId}; -- 示例2:经理查看本部门及下属部门的报销单 SELECT * FROM reimbursements WHERE dept_id IN (SELECT id FROM depts WHERE path LIKE CONCAT(#{currentDeptPath}, '%')); -- 注:depts表的path字段存储部门路径(如"1/2/3"),通过LIKE匹配所有子部门 -
代码层面:在Service层通过逻辑判断过滤数据。
// 示例:列级权限过滤(隐藏敏感字段) public List<CustomerVO> getCustomerList(Long userId) { List<Customer> customers = customerMapper.selectByUserId(userId); // 行级过滤已在SQL中实现 return customers.stream().map(customer -> { CustomerVO vo = new CustomerVO(); vo.setName(customer.getName()); vo.setPhone(customer.getPhone()); // 普通用户隐藏身份证号,管理员可见 if (isAdmin(userId)) { vo.setIdCard(customer.getIdCard()); } else { vo.setIdCard(desensitize(customer.getIdCard())); // 脱敏处理:110****5678 } return vo; }).collect(Collectors.toList()); }
2. 优缺点与适用场景
-
优点:
- 开发速度快,无需设计复杂权限模型;
- 性能优异,无额外权限查询和解析开销。
-
缺点:
- 灵活性极差:规则变更需修改代码、重新部署;
- 维护成本高:权限规则分散在多处SQL/代码中,难以统一管理;
- 扩展性差:新增角色或数据规则时,需重复开发类似逻辑。
-
适用场景:
- 初创公司,业务规则简单且稳定;
- 临时需求,短期内无需迭代;
- 边缘功能,使用频率低且影响范围小。
方案二:RBAC+数据范围——主流企业的折中方案
基于RBAC(角色基础访问控制)模型,为角色绑定“数据范围”,实现权限的动态配置。该方案平衡了灵活性与复杂度,覆盖80%以上的企业级场景(如基于组织架构的数据归属)。
1. 核心设计:角色-权限点-数据范围的三元关联
-
核心思想:角色(Role)在拥有某个权限点(Permission)的同时,绑定一个“数据范围”(Data Scope),表示该角色通过此权限点能访问的数据范围。
-
表结构设计:
表名 核心字段 说明 rolerole_id,role_name角色表(如“销售专员”“部门经理”) permissionperm_id,perm_code,name权限点表(如“订单查看”“客户编辑”) role_perm_scoperole_id,perm_id,data_scope角色-权限点-数据范围关联表
2. 数据范围的标准化定义
data_scope字段定义了预配置的数据范围,常见取值如下:
data_scope 值 |
含义 | 适用场景 | SQL条件示例(简化) |
|---|---|---|---|
SELF |
仅本人数据 | 个人数据(如自己的报销单) | create_user_id = #{currentUserId} |
DEPT |
本部门数据 | 部门内数据(如本部门的订单) | dept_id = #{currentDeptId} |
DEPT_AND_BELOW |
本部门及下属部门数据 | 管理层查看下属数据 | dept_id IN (#{currentDeptAndChildrenIds}) |
ORG |
本机构数据(如子公司) | 跨部门但同机构的数据 | org_id = #{currentOrgId} |
ALL |
所有数据 | 管理员权限 | 无额外条件(或1=1) |
CUSTOM(dept1,dept2) |
指定部门数据(自定义范围) | 区域负责人查看指定区域数据 | dept_id IN (dept1, dept2, ...) |
3. 技术实现流程
以“销售经理通过‘订单查看’权限查看本部门及下属部门订单”为例:
-
权限查询:用户登录后,查询其角色绑定的权限点及数据范围:
SELECT rp.perm_code, rp.data_scope FROM role_perm_scope rp JOIN user_role ur ON rp.role_id = ur.role_id WHERE ur.user_id = #{currentUserId} AND rp.perm_code = 'ORDER_VIEW'; -- 结果:perm_code=ORDER_VIEW, data_scope=DEPT_AND_BELOW -
数据范围解析:根据
data_scope计算具体的过滤条件:- 获取当前用户的部门ID:
currentDeptId = 3; - 查询部门3的所有下属部门ID:
deptIds = [3,4,5](通过部门树表递归查询); - 生成SQL条件:
dept_id IN (3,4,5)。
- 获取当前用户的部门ID:
-
动态拼接SQL:通过MyBatis动态SQL将条件嵌入查询:
<select id="listOrders" parameterType="map" resultType="Order"> SELECT * FROM orders <where> <!-- 其他业务条件 --> <if test="dataScope == 'DEPT_AND_BELOW'"> AND dept_id IN (#{deptIds}) </if> <if test="dataScope == 'SELF'"> AND create_user_id = #{currentUserId} </if> </where> </select>
4. 优缺点与适用场景
-
优点:
- 配置灵活:通过后台管理界面可动态调整角色的数据范围,无需改代码;
- 覆盖主流场景:尤其适合基于组织架构(个人、部门、机构)的数据归属场景;
- 易于理解:数据范围标准化,管理员可直观配置。
-
缺点:
- 扩展性有限:无法表达复杂规则(如“金额>100万且状态为已审批”);
- 数据范围固化:新增范围类型(如基于区域、时间)需修改代码。
-
适用场景:
- 中大型企业,基于组织架构的数据权限控制;
- 规则以“数据归属”为主(如个人、部门、机构);
- 需动态调整权限,但规则不复杂的场景。
方案三:策略表达式模式——复杂规则的灵活表达
当数据范围无法满足复杂规则(如结合业务属性、多条件组合)时,采用“策略表达式”模式,通过配置化的条件组合定义数据权限。
1. 核心设计:策略-条件组-条件的三级结构
-
核心思想:将数据权限规则抽象为“策略”,策略由多个“条件组”构成,条件组由多个“原子条件”构成,通过逻辑运算符(AND/OR)组合,实现任意复杂规则。
-
表结构设计:
表名 核心字段 说明 data_policypolicy_id,policy_code,enabled策略表(如“高金额订单查看策略”) data_policy_bindingsubject_type,subject_id,perm_id,policy_id绑定表(关联用户/角色与策略) data_policy_groupgroup_id,policy_id,logical_op条件组表( logical_op:AND/OR)data_policy_conditioncond_id,group_id,field,operator,value_type,value_expr原子条件表(字段、运算符、值)
2. 核心字段说明
subject_type/subject_id:绑定主体(user/role,如subject_type='role',subject_id='manager');logical_op:条件组内的逻辑关系(AND:所有条件需满足;OR:任一条件满足);field:数据字段(如amount“金额”、status“状态”);operator:运算符(=,!=,>,<,IN,LIKE等);value_type:值类型(const常量/userAttr用户属性,如value_type='userAttr',value_expr='deptId'表示取当前用户的部门ID);value_expr:值表达式(如500000、'approved'、'deptId')。
3. 示例:“高金额订单查看策略”
需求:角色finance_manager通过ORDER_VIEW权限点,只能查看“金额>50万且(状态为已审批或区域为华北)”的订单。
-
步骤1:创建策略及条件组
data_policy:policy_id=1,policy_code='HIGH_AMOUNT_ORDER',enabled=true;data_policy_group:- 组1:
group_id=1,policy_id=1,logical_op='AND'(金额条件组); - 组2:
group_id=2,policy_id=1,logical_op='OR'(状态/区域条件组)。
- 组1:
-
步骤2:添加原子条件
data_policy_condition:- 条件1:
group_id=1,field='amount',operator='>',value_type='const',value_expr='500000'(金额>50万); - 条件2:
group_id=2,field='status',operator='=',value_type='const',value_expr='approved'(状态=已审批); - 条件3:
group_id=2,field='region',operator='=',value_type='const',value_expr='华北'(区域=华北)。
- 条件1:
-
步骤3:绑定策略
data_policy_binding:subject_type='role',subject_id='finance_manager',perm_id='ORDER_VIEW',policy_id=1。
4. 技术实现流程
-
策略查询:用户访问
ORDER_VIEW权限时,查询其绑定的策略:SELECT p.policy_id FROM data_policy_binding b JOIN data_policy p ON b.policy_id = p.policy_id WHERE b.subject_type='role' AND b.subject_id='finance_manager' AND b.perm_id='ORDER_VIEW' AND p.enabled=true; -
条件解析:递归查询策略关联的条件组和原子条件,转换为抽象语法树(AST):
- 解析结果:
(amount > 500000) AND (status = 'approved' OR region = '华北')。
- 解析结果:
-
表达式转换:将AST转换为SQL条件(使用SpEL、OGNL等表达式引擎):
WHERE (amount > 500000) AND (status = 'approved' OR region = '华北') -
动态拼接:将SQL条件嵌入查询,返回符合条件的订单。
5. 优缺点与适用场景
-
优点:
- 灵活性极强:支持任意条件组合(业务属性、多维度规则);
- 完全配置化:新增/修改规则无需改代码,通过后台配置完成;
- 可复用性高:策略可绑定到多个用户/角色,避免重复配置。
-
缺点:
- 技术复杂度高:需实现表达式解析、SQL转换、性能优化;
- 运维成本高:管理员需理解条件逻辑,配置门槛高;
- 性能开销:策略查询、条件解析会增加查询耗时(需缓存优化)。
-
适用场景:
- 大型企业,业务规则复杂(如金融、电商的多维度数据权限);
- 规则变更频繁,需动态调整;
- 需支持跨组织、跨业务线的复杂条件组合。
方案四:ABAC模型——平台级系统的终极方案
ABAC(属性基础访问控制)模型从“属性”出发定义权限,通过用户、资源、环境的属性动态决策,适用于超复杂、高动态的平台级系统(如云平台、低代码平台)。
1. 核心思想:属性驱动的动态决策
-
属性分类:
- 用户属性:用户的角色、部门、职位、区域、权限等级等(如
user.dept = '销售部'); - 资源属性:数据的创建人、所属部门、金额、状态、类型等(如
resource.amount > 100000); - 环境属性:访问时间、IP地址、终端类型等(如
env.time BETWEEN '09:00' AND '18:00')。
- 用户属性:用户的角色、部门、职位、区域、权限等级等(如
-
决策逻辑:“当属性满足某条件时,允许/拒绝访问”,例如:
- “如果用户是部门经理(用户属性),且资源所属部门与用户部门一致(资源属性),且访问时间在工作时间(环境属性),则允许查看”。
2. 表结构设计:策略-属性条件的关联
ABAC的表结构在策略表达式模式基础上,强化“资源”和“动作”的绑定,实现更精准的策略定位:
| 表名 | 核心字段 | 说明 |
|---|---|---|
abac_policy |
policy_id, resource, action, enabled |
策略表(resource:资源类型;action:操作) |
abac_binding |
subject_type, subject_id, policy_id |
绑定表(关联用户/角色与策略) |
abac_policy_group |
group_id, policy_id, logical_op |
条件组表(AND/OR) |
abac_policy_condition |
cond_id, group_id, attr_type, attr_key, operator, value_expr |
条件表(attr_type:user/resource/env) |
3. 示例:“云平台实例访问策略”
需求:在阿里云平台中,“项目管理员”角色只能在工作时间查看本项目下状态为“运行中”的ECS实例。
- 策略定义:
abac_policy:policy_id=1,resource='ecs',action='view',enabled=true;abac_binding:subject_type='role',subject_id='project_admin',policy_id=1;abac_policy_group:group_id=1,policy_id=1,logical_op='AND';abac_policy_condition:- 条件1:
attr_type='user',attr_key='projectId',operator='=',value_expr='resource.projectId'(用户项目ID=资源项目ID); - 条件2:
attr_type='resource',attr_key='status',operator='=',value_expr='running'(资源状态=运行中); - 条件3:
attr_type='env',attr_key='time',operator='BETWEEN',value_expr='09:00,18:00'(访问时间在工作时间)。
- 条件1:
4. 技术实现:动态属性匹配与决策
-
属性收集:访问请求触发时,收集用户、资源、环境属性:
- 用户属性:
user.projectId=1001; - 资源属性:
resource.projectId=1001,resource.status='running'; - 环境属性:
env.time='10:30'。
- 用户属性:
-
策略匹配:查询
resource='ecs'且action='view'且绑定subject_id='project_admin'的策略。 -
条件评估:使用规则引擎(如Drools、Easy Rules)评估条件是否满足:
- 条件1:
1001 = 1001→ 满足; - 条件2:
'running' = 'running'→ 满足; - 条件3:
'10:30' BETWEEN '09:00' AND '18:00'→ 满足; - 结果:所有条件满足,允许访问。
- 条件1:
-
数据过滤:生成数据过滤条件(如
project_id=1001 AND status='running'),应用到查询。
5. 优缺点与适用场景
-
优点:
- 极致灵活:支持用户、资源、环境的任意属性组合,覆盖几乎所有场景;
- 平台级适配:适合多租户、多资源类型的复杂系统;
- 动态扩展:新增属性或规则无需修改核心代码。
-
缺点:
- 技术门槛极高:需实现属性收集、规则引擎、高性能匹配;
- 运维复杂:策略配置需要专业知识,易出错;
- 性能挑战:大规模策略匹配和条件评估可能成为瓶颈。
-
适用场景:
- 云平台(如AWS、阿里云)、低代码平台;
- 多租户系统,需为每个租户定制数据权限;
- 规则超复杂且动态变化的场景。
三、数据权限的技术落地与优化
无论选择哪种方案,数据权限的落地都需解决“动态条件生成”“性能优化”“权限审计”三大核心问题。
1. 动态条件生成:从权限规则到SQL
- 行级条件:通过拦截器(如MyBatis的
Interceptor)在SQL执行前动态拼接WHERE子句。- 示例:使用MyBatis拦截器获取当前用户的权限条件,通过
BoundSql修改SQL。
- 示例:使用MyBatis拦截器获取当前用户的权限条件,通过
- 列级条件:
- 隐藏字段:动态修改
SELECT子句,移除无权限的字段; - 脱敏处理:使用类型处理器(TypeHandler)对返回字段进行脱敏(如
id_card字段转换为110****5678)。
- 隐藏字段:动态修改
2. 性能优化:避免权限成为系统瓶颈
- 缓存策略:
- 缓存用户-权限-数据范围的关联关系(如Redis缓存
user:1:perm:ORDER_VIEW → DEPT_AND_BELOW); - 缓存解析后的策略条件(如将表达式解析结果缓存,避免重复解析)。
- 缓存用户-权限-数据范围的关联关系(如Redis缓存
- 索引优化:为数据权限过滤字段(如
dept_id、create_user_id)建立索引,加速条件查询。 - 异步加载:复杂策略的解析可异步进行,避免阻塞主查询流程。
3. 权限审计:满足合规与追溯需求
- 日志记录:记录用户访问的数据(资源类型、记录ID、访问时间、操作类型),支持事后审计;
- 敏感操作告警:对高权限操作(如查看全量客户数据)触发告警,防止数据泄露。
四、方案选择指南:匹配业务复杂度
| 业务复杂度 | 推荐方案 | 核心考量因素 |
|---|---|---|
| 简单(固定规则) | 硬编码 | 开发速度优先,规则极少变更 |
| 中等(组织架构为核心) | RBAC+数据范围 | 平衡灵活性与复杂度,覆盖组织内数据归属 |
| 复杂(多条件组合) | 策略表达式模式 | 支持业务属性组合,配置化调整规则 |
| 超复杂(平台级) | ABAC模型 | 极致灵活,支持多维度属性与动态规则 |
总结
数据权限控制是企业级系统数据安全的核心防线,其设计需在“灵活性”“性能”“运维成本”之间找到平衡。从硬编码到ABAC模型,方案的演进反映了业务对数据精细化管理的需求升级。实际落地时,应先明确业务规则的复杂度和变更频率,选择适配的方案——而非盲目追求“最灵活”的模型。同时,需关注动态条件生成、性能优化和权限审计,确保数据权限系统既安全又高效。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)