Skip to content

行级安全

数据库原生的行级安全(Row-Level Security,RLS)在数据库引擎层面强制执行数据过滤。与数据墙DBW 的数据库策略不同,原生 RLS 的安全边界在数据库内部——即使绕过数据墙DBW 直接连接数据库,过滤仍然生效。数据墙DBW 通过会话上下文将用户声明的身份信息传递给数据库,由数据库的安全策略完成过滤。

IMPORTANT

本章描述的方法使用 SQL Server 原生 RLS。目前仅 SQL Server 支持此功能。

数据库策略 vs 原生 RLS

维度数据库策略原生 RLS
过滤执行者数据墙DBW 引擎SQL Server 引擎
配置位置dab-config.jsonSQL Server 安全策略和函数
绕过 DAB 访问策略不生效策略仍然生效
跨数据库SQL Server、PostgreSQL、MySQL仅 SQL Server
灵活性按角色和操作配置按会话上下文配置
性能WHERE 条件追加谓词函数,可建立索引

工作原理

text
1. 数据墙DBW 启用 set-session-context
2. 请求到达,引擎从 JWT 令牌中提取声明
3. 引擎调用 sp_set_session_context 将声明注入 SQL Server 会话
4. 数据库执行查询
5. SQL Server 的安全策略根据 SESSION_CONTEXT 值自动过滤行
6. 过滤后的结果返回给数据墙DBW

第一步:创建数据库对象

准备测试表和示例数据:

sql
CREATE TABLE dbo.Revenues (
    id INT PRIMARY KEY,
    category VARCHAR(100) NOT NULL,
    revenue INT,
    accessible_role VARCHAR(100) NOT NULL
);

INSERT INTO dbo.Revenues VALUES
    (1, 'Book',     5000,  'Oscar'),
    (2, 'Comics',   10000, 'Oscar'),
    (3, 'Journals', 20000, 'Hannah'),
    (4, 'Series',   40000, 'Hannah');

在本例中,accessible_role 列存储了可以访问该行的角色名。只有拥有对应角色的用户才能看到该行。

第二步:创建安全谓词函数

创建一个内联表值函数作为安全谓词:

sql
CREATE FUNCTION dbo.RevenuesPredicate(@accessible_role VARCHAR(100))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS fn_securitypredicate_result
WHERE
    @accessible_role = CAST(SESSION_CONTEXT(N'roles') AS VARCHAR(100));
关键点说明
SESSION_CONTEXT(N'roles')读取会话上下文中 roles 键的值
WITH SCHEMABINDING必需——防止底层对象被意外修改,确保安全策略不可绕过
返回值函数返回 1 表示该行应被包含,不返回表示该行被过滤

第三步:创建安全策略

将安全谓词函数绑定到目标表:

sql
CREATE SECURITY POLICY dbo.RevenuesSecurityPolicy
ADD FILTER PREDICATE dbo.RevenuesPredicate(accessible_role)
ON dbo.Revenues;

创建后,对该表的所有查询都会自动经过谓词函数过滤。即使是 SELECT * FROM dbo.Revenues 也不会返回全部行——安全策略在数据库引擎层面强制执行。

第四步:配置数据墙DBW

启用会话上下文,将 JWT 声明传递给数据库:

bash
dab init \
  --database-type "mssql" \
  --connection-string "<connection-string>" \
  --set-session-context true

或在已有配置中修改:

json
{
  "data-source": {
    "database-type": "mssql",
    "options": {
      "set-session-context": true
    }
  }
}

添加实体:

bash
dab add revenue --source "dbo.Revenues" --permissions "Authenticated:read"

第五步:测试行级过滤

启动服务:

bash
dab start

以 Oscar 角色请求

bash
curl -H "X-MS-API-ROLE: Oscar" http://localhost:5000/api/revenue

只返回 Oscar 可访问的行:

json
{
  "value": [
    { "id": 1, "category": "Book",    "revenue": 5000  },
    { "id": 2, "category": "Comics",  "revenue": 10000 }
  ]
}

以 Hannah 角色请求

bash
curl -H "X-MS-API-ROLE: Hannah" http://localhost:5000/api/revenue

只返回 Hannah 可访问的行:

json
{
  "value": [
    { "id": 3, "category": "Journals", "revenue": 20000 },
    { "id": 4, "category": "Series",   "revenue": 40000 }
  ]
}

Authenticated 角色(默认)

bash
curl http://localhost:5000/api/revenue

返回空数组——没有任何行的 accessible_role'Authenticated'

GraphQL 同样生效

bash
curl -X POST http://localhost:5000/graphql \
  -H "Content-Type: application/json" \
  -H "X-MS-API-ROLE: Oscar" \
  -d '{"query": "{ revenues { items { id category revenue } } }"}'

原生 RLS 对 REST 和 GraphQL 同时生效,因为过滤发生在数据库引擎层。

引擎实际执行的 SQL

启用了 set-session-context 后,数据墙DBW 在每次查询前执行:

sql
EXEC sp_set_session_context 'roles', 'Oscar', @read_only = 0;
SELECT * FROM dbo.Revenues;

roles 是声明键,'Oscar' 是从 JWT 令牌中提取的实际值。安全策略中的 SESSION_CONTEXT(N'roles') 读取到这个值后与表的 accessible_role 列比较,完成过滤。

所有 JWT 声明都会作为键值对传入会话上下文。常见的注入声明包括 rolessubuserId 等,具体取决于身份提供程序的输出。

常见使用场景

场景会话上下文键安全策略逻辑
基于角色的过滤roles比较用户角色与行的可访问角色
多租户隔离tenantId比较用户租户 ID 与行的租户 ID
用户级数据隔离subuserId比较用户 ID 与行的所有者 ID

在 SQL 中直接验证

可以用 SQL 验证安全策略是否正确工作:

sql
-- 设置会话上下文
EXEC sp_set_session_context 'roles', 'Oscar';

-- 查询——安全策略自动过滤
SELECT * FROM dbo.Revenues;
-- 只返回 Oscar 的两行

-- 检查当前会话上下文值
SELECT SESSION_CONTEXT(N'roles') AS current_role;
-- 返回 'Oscar'

与存储过程配合

安全策略对存储过程中的查询同样生效:

sql
CREATE PROCEDURE dbo.GetRevenuesForCurrentRole
AS
BEGIN
    SET NOCOUNT ON;
    SELECT id, category, revenue
    FROM dbo.Revenues;
END

由于安全策略是绑定到表上而不是绑定到查询方式,无论通过直接 SELECT、存储过程还是视图访问 dbo.Revenues,过滤都会执行。

缓存注意事项

WARNING

启用 set-session-context 后,数据墙DBW 会自动禁用该数据源的响应缓存。因为不同用户的会话上下文不同,同一查询的结果因用户而异,缓存结果不能跨用户共享。

对于高流量场景,建议:

  • 为谓词函数涉及的列(如 accessible_role)创建索引。
  • 评估是否可以用数据墙DBW 数据库策略替代原生 RLS——数据库策略不影响缓存。
  • 做性能测试确认安全策略对查询计划的影响。

存储过程使用会话上下文

存储过程可以直接读取 SESSION_CONTEXT 实现自定义逻辑:

sql
CREATE PROCEDURE dbo.GetRevenuesForCurrentRole
AS
BEGIN
    DECLARE @role VARCHAR(100) = CAST(SESSION_CONTEXT(N'roles') AS VARCHAR(100));
    SELECT id, category, revenue
    FROM dbo.Revenues
    WHERE accessible_role = @role;
END

这种方式依赖存储过程内部的显式 WHERE 过滤,与安全策略的自动过滤互为补充。

故障排查

问题检查方法
没有返回结果SELECT * FROM sys.security_policies 确认策略已启用;SELECT SESSION_CONTEXT(N'roles') 确认上下文值正确
返回了所有行检查安全策略的 STATE 没有设为 OFF;确认谓词函数对匹配行返回 1
性能下降为谓词列创建索引;临时禁用安全策略对比基线性能
缓存相关报错这是预期行为——set-session-context 会自动禁用缓存

清理

sql
DROP SECURITY POLICY IF EXISTS dbo.RevenuesSecurityPolicy;
DROP FUNCTION IF EXISTS dbo.RevenuesPredicate;
DROP PROCEDURE IF EXISTS dbo.GetRevenuesForCurrentRole;
DROP TABLE IF EXISTS dbo.Revenues;

下一步

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