Skip to content

数据访问策略

数据库策略是数据墙DBW 提供的行级过滤机制。策略表达式在引擎层被解析并转换为 SQL WHERE 条件,追加到每次数据库查询中。用户只能看到策略允许的数据行。

工作原理

text
请求 → 引擎确定角色 → 查找策略表达式
  → 解析 @claims 引用(从 JWT 令牌中提取实际值)
  → 生成 WHERE 条件
  → 追加到 SQL 查询 → 数据库执行 → 返回过滤后的结果

数据库策略在数据墙DBW 引擎层工作。引擎不依赖数据库原生的行级安全功能——策略被转换为标准 SQL 查询谓词,因此对所有支持的数据库(SQL Server、PostgreSQL、MySQL)都有效。

策略语法

策略使用 OData 风格的表达式,支持字段引用、声明引用和逻辑组合:

语法元素格式示例
字段引用@item.<field>@item.status
声明引用@claims.<claimType>@claims.userId
相等eq@item.ownerId eq @claims.userId
不等ne@item.status ne 'deleted'
大于gt@item.price gt 100
大于等于ge@item.year ge 2000
小于lt@item.inventory lt 10
小于等于le@item.price le 50
逻辑与and@item.active eq true and @item.status eq 'published'
逻辑或or@item.role eq 'admin' or @item.role eq 'editor'

IMPORTANT

如果在策略中引用了某个 JWT 声明,但当前请求的令牌中不存在该声明,引擎会直接拒绝请求并返回 403 Forbidden。不是返回空结果——是直接拒绝。

策略适用的操作

操作是否支持原因
readSELECT 语句可追加 WHERE 条件
updateUPDATE 语句可追加 WHERE 条件
deleteDELETE 语句可追加 WHERE 条件
createINSERT 语句不支持 WHERE 条件
execute存储过程不接受外部查询谓词

配置语法

策略按角色和操作定义在实体权限中:

json
{
  "entities": {
    "<entity-name>": {
      "permissions": [
        {
          "role": "<role-name>",
          "actions": [
            {
              "action": "read",
              "policy": {
                "database": "<predicate-expression>"
              }
            }
          ]
        }
      ]
    }
  }
}

配置示例

用户只能访问自己的数据

json
{
  "Order": {
    "permissions": [
      {
        "role": "customer",
        "actions": [
          {
            "action": "read",
            "policy": {
              "database": "@item.customerId eq @claims.userId"
            }
          }
        ]
      }
    ]
  }
}

当用户 user123 查询订单时,生成的 SQL 包含:

sql
SELECT * FROM Orders WHERE customerId = 'user123'

只返回已发布的内容

固定值过滤,不依赖用户声明:

json
{
  "Article": {
    "permissions": [
      {
        "role": "anonymous",
        "actions": [
          {
            "action": "read",
            "policy": {
              "database": "@item.status eq 'published'"
            }
          }
        ]
      }
    ]
  }
}

匿名用户只能看到已发布的文章,草稿自动被过滤。

同一策略应用于多个操作

json
{
  "Document": {
    "permissions": [
      {
        "role": "author",
        "actions": [
          {
            "action": "read",
            "policy": {
              "database": "@item.authorId eq @claims.sub"
            }
          },
          {
            "action": "update",
            "policy": {
              "database": "@item.authorId eq @claims.sub"
            }
          },
          {
            "action": "delete",
            "policy": {
              "database": "@item.authorId eq @claims.sub"
            }
          }
        ]
      }
    ]
  }
}

作者只能读取、更新和删除自己的文档。

复合条件

json
{
  "report": {
    "permissions": [
      {
        "role": "manager",
        "actions": [
          {
            "action": "read",
            "policy": {
              "database": "@item.departmentId eq @claims.departmentId and @item.status ne 'archived'"
            }
          }
        ]
      }
    ]
  }
}

部门经理只能看到本部门且非归档状态的报告。

不同角色不同策略

json
{
  "Order": {
    "permissions": [
      {
        "role": "customer",
        "actions": [
          {
            "action": "read",
            "policy": {
              "database": "@item.customerId eq @claims.userId"
            }
          }
        ]
      },
      {
        "role": "admin",
        "actions": [
          {
            "action": "read",
            "policy": {
              "database": "@item.region eq @claims.region"
            }
          },
          "create",
          "update"
        ]
      }
    ]
  }
}

customer 只能看到自己的订单,admin 能看到本区域的全部订单并可以创建和更新。

策略与字段控制的组合

一个动作可以同时配置字段过滤和行级策略:

json
{
  "action": "read",
  "fields": {
    "include": ["*"],
    "exclude": ["salary", "internalNotes"]
  },
  "policy": {
    "database": "@item.departmentId eq @claims.departmentId"
  }
}

引擎先应用字段过滤(隐藏列),再应用行级策略(过滤行)。两者独立生效,互不干扰。

CLI 配置

bash
dab update Order \
  --permissions "customer:read" \
  --policy-database "@item.customerId eq @claims.userId"

带特殊字符时用引号包裹整个表达式,具体引号规则取决于所用 Shell。

claims 替换的实现

当请求到达时,引擎执行以下替换:

  1. 从 JWT 令牌中提取所有声明键值对。
  2. 扫描策略表达式中的 @claims.<type> 模式。
  3. 将每个 @claims.<type> 替换为令牌中对应声明的实际值。
  4. 替换后的纯值表达式被转换为 SQL WHERE 条件。

例如,策略 @item.ownerId eq @claims.userId,令牌中 userId: "user123"

@item.ownerId eq @claims.userId
           ↓ 替换
@item.ownerId eq 'user123'
           ↓ 转换
WHERE [ownerId] = 'user123'

常见声明类型

声明来源示例值
subJWT 标准,用户主体标识ffffffff-eeee-dddd...
userId自定义,用户 IDuser_12345
email用户邮箱user@example.com
name用户显示名张三
roles角色数组["reader", "editor"]
tenantId多租户标识tenant_abc
departmentId部门标识dept_sales

声明的可用性取决于你的身份提供程序配置。策略中引用的每个声明都必须在 JWT 中存在。

字段名引用

策略中的 @item.<field> 使用 API 层字段名。如果配置了字段别名(fields[].alias),应使用别名:

json
{
  "fields": [
    { "name": "customer_id", "alias": "customerId" }
  ]
}

策略中引用 @item.customerId,而不是 @item.customer_id

数据库策略 vs 数据库原生 RLS

维度数据墙DBW 数据库策略数据库原生 RLS
工作层级数据墙DBW 引擎层数据库引擎层
配置位置dab-config.jsonSQL Server 安全策略
跨数据库支持SQL Server、PostgreSQL、MySQL各数据库自行实现
实现方式追加 WHERE 条件数据库安全策略谓词
灵活性按角色和操作配置按数据库用户配置

数据库策略的优势在于配置简单、跨数据库一致、无需修改数据库对象。数据库原生 RLS 的优势在于安全边界在数据库内部,即使绕过数据墙DBW 直接访问数据库,过滤仍然生效。两者可以配合使用——数据墙DBW 策略提供应用层过滤,数据库原生 RLS 提供最后一道防线。

故障排查

问题可能原因检查方法
策略未生效角色不匹配确认请求的最终角色名与策略中 role 一致
始终 403引用的声明不存在解码 JWT 检查策略中引用的声明是否存在
语法错误表达式不符合 OData 规范确认使用了 eq 而非 =,使用 and 而非 &&
字段名错误使用了数据库列名而非 API 字段名检查 fields.alias 配置,策略中使用别名

设置会话上下文

json
{
  "data-source": {
    "options": {
      "set-session-context": true
    }
  }
}

下一步

数据墙DBW 产品文档与开发指南。