Java数据权限流程如何规范

wen java案例 30

本文目录导读:

Java数据权限流程如何规范

  1. 核心设计原则 (规范化的基石)
  2. 规范化的数据权限建模
  3. 规范化流程架构 (从配置到执行)
  4. 规范化的常见策略模型
  5. 规范化流程中的关键注意事项
  6. 一张图看懂规范流程

在Java企业级应用中,数据权限(也称为行级权限)的规范化设计是核心安全需求之一,它区别于功能权限(你能访问哪个菜单),定义了用户能看到哪些数据记录(销售人员只能看自己的订单,部门经理看本部门,CEO看全部)。

为了让数据权限流程规范化,需要从数据模型、权限规则引擎、拦截机制三个核心层面进行设计,以下是经过业界验证的规范流程和最佳实践。

核心设计原则 (规范化的基石)

  1. 权限与业务逻辑分离:数据权限规则不应散落在SQL语句或业务代码中,必须集中管理。
  2. 规则可配置:管理员应能通过界面配置规则(如:用户只能查看 部门ID = 当前用户部门ID 的数据),而非硬编码。
  3. 性能优先:数据权限过滤尽量在数据库层完成(通过SQL拼接或拦截),避免将全量数据加载到内存后再过滤。
  4. 最小粒度原则:默认无权限,有规则才可见。

规范化的数据权限建模

数据权限的规则通常基于以下几个核心维度(实体):

  • 用户 (User):谁在操作?
  • 角色/岗位 (Role/Position):用户属于哪个职能?
  • 组织架构 (Org/Dept):用户属于哪个部门?部门之间是否有层级关系?
  • 数据归属者 (Owner):数据属于谁创建?
  • 数据属性 (如区域、项目):数据本身携带的地区、项目ID等字段。

规范化流程架构 (从配置到执行)

一个完整的规范流程包含以下步骤:

阶段1:规则定义与配置(后台管理)

这是规范化最重要的一环,通常由一个权限管理中心负责。

  1. 规则实体设计

    • 数据权限规则表
      • ruleId
      • ruleName (查看本部门订单)
      • type (SELFDEPTORG_TREECUSTOM)
      • scopeTable (应用的表名,如 order)
      • conditionExpression (控制条件,如 dept_id = #{currentUser.deptId}) 或使用策略类名。
    • 角色-规则关联表:将规则绑定到角色上。
    • 用户-规则白名单/黑名单:允许对特定用户进行例外处理。
  2. 配置界面

    • 提供可视化配置,选择:作用表 -> 控制字段 (如 dept_id) -> 控制值来源 (如 当前用户所在部门ID)。

阶段2:上下文解析与策略匹配(运行时)

当用户发起请求时,系统需要解析“我是谁”。

  1. 获取用户上下文

    从SecurityContext(Spring Security/Shiro)获取:用户ID、部门ID列表、角色编码。

  2. 加载用户的数据权限规则
    • 查询该用户拥有的所有角色关联的数据权限规则。
    • 合并规则(通常是取并集,部分复杂场景取交集)。

阶段3:SQL拦截与注入(核心执行)

这是规范化的技术难点,一般采用AOP + MyBatis拦截器JPA过滤器

  1. 拦截点

    • MyBatis:实现 Interceptor 接口,拦截 Executor.query() 方法,在SQL执行前修改 MappedStatement 中的 BoundSql
    • JPA:使用 @Filter 注解或 EntityListeners。
  2. SQL重写规范

    • 规则:假设用户有一条规则 只能看本部门数据
    • 逻辑:找到SQL中操作的表(如 t_order),在其WHERE条件后追加 AND (t_order.dept_id = ?)
    • 参数注入:将当前用户的部门ID作为预编译参数传入,防止SQL注入。
    • 处理JOIN:如果主表需要根据关联表过滤(只能看自己参与的项目下的任务),则需要修改JOIN条件或WHERE子查询。
    // 伪代码示例:MyBatis DataAuthInterceptor
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        BoundSql boundSql = ms.getBoundSql(parameter);
        String originalSql = boundSql.getSql();
        // 1. 解析SQL,获取主表名
        String mainTableName = parseTableName(originalSql);
        // 2. 根据当前用户,获取针对该表的数据权限 WHERE 子句
        String filterCondition = dataAuthService.getFilterCondition(用户ID, mainTableName);
        // 3. 将条件拼接到SQL的WHERE后(注意处理已有WHERE的情况,用 AND 连接)
        String newSql = injectWhereCondition(originalSql, filterCondition);
        // 4. 修改MappedStatement中的SQL
        // ... (使用反射替换 BoundSql)
        return invocation.proceed();
    }

规范化的常见策略模型

为了让流程更规范,建议将数据权限定义为以下几种标准策略,并开发对应的策略类:

  1. 仅看自己
    • SQL条件:WHERE create_by = #{userId}
  2. 仅看本部门
    • SQL条件:WHERE dept_id = #{currentDeptId}
  3. 仅看本部门及子部门
    • SQL条件:WHERE dept_id IN (SELECT id FROM sys_dept WHERE path LIKE concat('0', #{deptId}, '%'))
    • 需要部门表支持path字段(如:0/1001/1002/)。
  4. 自定义字段过滤
    • 根据数据中的某个字段(如 area_code)与当前用户的属性(如 负责的区域)匹配。
  5. 基于用户扩展属性

    如:查看“销售A组”创建的数据,基于用户组织的自定义属性。

规范化流程中的关键注意事项

  1. 防止权限绕过

    • 后台服务调用(Feign/API):服务间调用时,必须传递用户上下文Token,否则数据权限拦截器会因“无当前用户”而拒绝查询(或默认返回空)。
    • Excel 导出/定时任务:导出和后台作业同样需要注入权限,定时任务通常使用超级管理员权限,需在代码中显式标记。
  2. 性能优化

    • 索引:被过滤的字段(如 dept_idcreate_by)必须加索引。
    • 规则缓存:用户的权限规则可以缓存在Redis中,避免每次查询都查数据库。
    • 复杂子查询:如果规则涉及子查询(如顶级组织树),可能导致SQL性能差,建议使用数据冗余(如将部门路径预先计算好存入表中)或物化视图
  3. 事务与缓存一致性

    当更新了用户的角色或数据权限规则后,需要及时清除该用户的权限规则缓存,避免用户看到不应看到的数据。

一张图看懂规范流程

[用户请求] -> [权限过滤器/安全上下文]
      |
      v
[功能权限校验] (你是否能访问这个接口?)
      | (通过)
      v
[业务逻辑层调用DAO/Mapper]
      |
      v
[数据权限拦截器 (MyBatis Interceptor / AOP)]
      |
      +---> 1. 解析当前SQL操作的表 (如: t_order)
      +---> 2. 从缓存/DB获取当前用户对 t_order 的数据权限规则
      +---> 3. 执行规则策略类 (如: OnlySelfDataStrategy)
      +---> 4. 生成 WHERE 子句 (order.create_by = 42)
      +---> 5. 动态拼接到原始SQL中,并传递参数
      |
      v
[数据库执行修改后的SQL]
      |
      v
[返回经过过滤的结果集]

一句话总结规范: 将数据权限抽象为可配置的规则,通过AOP拦截在运行时动态重写SQL,根据用户上下文注入过滤条件,确保数据访问在数据库层面就被严格限制。

抱歉,评论功能暂时关闭!