在企业级应用中,权限系统的核心目标是“让合适的人在合适的场景下访问合适的数据”。功能权限(“能做什么操作”)解决了访问的“动作边界”,而数据权限(“能操作哪些数据”)则定义了访问的“范围边界”。本文将从数据权限的核心构成出发,系统解析四种实现方案的设计细节、技术落地与适用场景,帮助架构师根据业务复杂度选择最优方案。

一、数据权限的核心构成:行级与列级的双重防护

数据权限通过“行级控制”和“列级控制”的组合,实现对数据访问的精细化管理。两者相互补充,共同构建数据安全的“双重防线”。

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(过滤本部门客户);
    • 列级:只返回namephone字段,id_cardaddress字段隐藏。

二、数据权限实现方案:从简单到复杂的演进

数据权限的实现方案随业务复杂度提升而演进,从“硬编码”到“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),表示该角色通过此权限点能访问的数据范围。

  • 表结构设计

    表名 核心字段 说明
    role role_id, role_name 角色表(如“销售专员”“部门经理”)
    permission perm_id, perm_code, name 权限点表(如“订单查看”“客户编辑”)
    role_perm_scope role_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. 技术实现流程

以“销售经理通过‘订单查看’权限查看本部门及下属部门订单”为例:

  1. 权限查询:用户登录后,查询其角色绑定的权限点及数据范围:

    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
    
  2. 数据范围解析:根据data_scope计算具体的过滤条件:

    • 获取当前用户的部门ID:currentDeptId = 3
    • 查询部门3的所有下属部门ID:deptIds = [3,4,5](通过部门树表递归查询);
    • 生成SQL条件:dept_id IN (3,4,5)
  3. 动态拼接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_policy policy_id, policy_code, enabled 策略表(如“高金额订单查看策略”)
    data_policy_binding subject_type, subject_id, perm_id, policy_id 绑定表(关联用户/角色与策略)
    data_policy_group group_id, policy_id, logical_op 条件组表(logical_op:AND/OR)
    data_policy_condition cond_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_policypolicy_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'(状态/区域条件组)。
  • 步骤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='华北'(区域=华北)。
  • 步骤3:绑定策略

    • data_policy_bindingsubject_type='role', subject_id='finance_manager', perm_id='ORDER_VIEW', policy_id=1
4. 技术实现流程
  1. 策略查询:用户访问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;
    
  2. 条件解析:递归查询策略关联的条件组和原子条件,转换为抽象语法树(AST):

    • 解析结果:(amount > 500000) AND (status = 'approved' OR region = '华北')
  3. 表达式转换:将AST转换为SQL条件(使用SpEL、OGNL等表达式引擎):

    WHERE (amount > 500000) AND (status = 'approved' OR region = '华北')
    
  4. 动态拼接:将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_policypolicy_id=1, resource='ecs', action='view', enabled=true
    • abac_bindingsubject_type='role', subject_id='project_admin', policy_id=1
    • abac_policy_groupgroup_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'(访问时间在工作时间)。
4. 技术实现:动态属性匹配与决策
  1. 属性收集:访问请求触发时,收集用户、资源、环境属性:

    • 用户属性:user.projectId=1001
    • 资源属性:resource.projectId=1001, resource.status='running'
    • 环境属性:env.time='10:30'
  2. 策略匹配:查询resource='ecs'action='view'且绑定subject_id='project_admin'的策略。

  3. 条件评估:使用规则引擎(如Drools、Easy Rules)评估条件是否满足:

    • 条件1:1001 = 1001 → 满足;
    • 条件2:'running' = 'running' → 满足;
    • 条件3:'10:30' BETWEEN '09:00' AND '18:00' → 满足;
    • 结果:所有条件满足,允许访问。
  4. 数据过滤:生成数据过滤条件(如project_id=1001 AND status='running'),应用到查询。

5. 优缺点与适用场景
  • 优点

    • 极致灵活:支持用户、资源、环境的任意属性组合,覆盖几乎所有场景;
    • 平台级适配:适合多租户、多资源类型的复杂系统;
    • 动态扩展:新增属性或规则无需修改核心代码。
  • 缺点

    • 技术门槛极高:需实现属性收集、规则引擎、高性能匹配;
    • 运维复杂:策略配置需要专业知识,易出错;
    • 性能挑战:大规模策略匹配和条件评估可能成为瓶颈。
  • 适用场景

    • 云平台(如AWS、阿里云)、低代码平台;
    • 多租户系统,需为每个租户定制数据权限;
    • 规则超复杂且动态变化的场景。

三、数据权限的技术落地与优化

无论选择哪种方案,数据权限的落地都需解决“动态条件生成”“性能优化”“权限审计”三大核心问题。

1. 动态条件生成:从权限规则到SQL

  • 行级条件:通过拦截器(如MyBatis的Interceptor)在SQL执行前动态拼接WHERE子句。
    • 示例:使用MyBatis拦截器获取当前用户的权限条件,通过BoundSql修改SQL。
  • 列级条件
    • 隐藏字段:动态修改SELECT子句,移除无权限的字段;
    • 脱敏处理:使用类型处理器(TypeHandler)对返回字段进行脱敏(如id_card字段转换为110****5678)。

2. 性能优化:避免权限成为系统瓶颈

  • 缓存策略
    • 缓存用户-权限-数据范围的关联关系(如Redis缓存user:1:perm:ORDER_VIEW → DEPT_AND_BELOW);
    • 缓存解析后的策略条件(如将表达式解析结果缓存,避免重复解析)。
  • 索引优化:为数据权限过滤字段(如dept_idcreate_user_id)建立索引,加速条件查询。
  • 异步加载:复杂策略的解析可异步进行,避免阻塞主查询流程。

3. 权限审计:满足合规与追溯需求

  • 日志记录:记录用户访问的数据(资源类型、记录ID、访问时间、操作类型),支持事后审计;
  • 敏感操作告警:对高权限操作(如查看全量客户数据)触发告警,防止数据泄露。

四、方案选择指南:匹配业务复杂度

业务复杂度 推荐方案 核心考量因素
简单(固定规则) 硬编码 开发速度优先,规则极少变更
中等(组织架构为核心) RBAC+数据范围 平衡灵活性与复杂度,覆盖组织内数据归属
复杂(多条件组合) 策略表达式模式 支持业务属性组合,配置化调整规则
超复杂(平台级) ABAC模型 极致灵活,支持多维度属性与动态规则

总结

数据权限控制是企业级系统数据安全的核心防线,其设计需在“灵活性”“性能”“运维成本”之间找到平衡。从硬编码到ABAC模型,方案的演进反映了业务对数据精细化管理的需求升级。实际落地时,应先明确业务规则的复杂度和变更频率,选择适配的方案——而非盲目追求“最灵活”的模型。同时,需关注动态条件生成、性能优化和权限审计,确保数据权限系统既安全又高效。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐