行级安全性

使用组成员资格或执行上下文来控制对数据库表中行的访问权限。

行级别安全性 (RLS) 简化了安全性的设计和编码。 它允许在应用程序中对数据行访问进行限制。 例如,限制用户,仅允许其访问与其部门相关的行;或限制客户,仅允许其访问与其公司相关的数据。

访问限制逻辑位于数据库层中,而不是在远离数据的另一个应用层中。 数据库系统会在用户每次尝试从任何层进行数据访问时应用访问限制。 此逻辑减少了安全系统的外围应用,使安全系统变得更加可靠和稳健。

使用 RLS,你可以向其他应用程序和用户仅提供对表的某个部分的访问权限。 例如,您可能希望:

  • 仅授予对满足某些条件的行的访问权限
  • 将某些列中的数据匿名化
  • 以上都是

注意

在表上启用 RLS 策略时,访问权限将完全被表上定义的 RLS 查询所取代。 访问限制适用于所有用户,包括数据库管理员和 RLS 创建者。 RLS 查询必须显式包含要授予其访问权限的所有类型的用户的定义。

有关详细信息,请参阅用于管理行级别安全性策略的管理命令

提示

以下函数通常用于 row_level_security 查询:

限制

  • 对于可以配置行级别安全性策略的表的数量没有限制。
  • 无法在外部表上配置行级别安全性策略。
  • 在以下情况下,无法对表启用 RLS 策略:

示例

限制对 Sales 表的访问

在名为 Sales 的表中,每行都包含有关销售的详细信息。 其中一列包含销售人员的姓名。 在 Sales 表上启用行级别安全性策略,而不是授予销售人员对此表中所有记录的访问权限,以便仅返回销售人员是当前用户的记录:

Sales | where SalesPersonAadUser == current_principal()

还可以过滤电子邮件地址:

Sales | where SalesPersonAadUser == current_principal() | extend EmailAddress = "****"

如果希望每个销售人员都能看到某个特定国家/地区的所有销售项,则可以定义类似于以下内容的查询:

let UserToCountryMapping = datatable(User:string, Country:string)
[
  "john@domain.com", "USA",
  "anna@domain.com", "France"
];
Sales
| where Country in ((UserToCountryMapping | where User == current_principal_details()["UserPrincipalName"] | project Country))

如果你有一个包含经理的组,则可能需要向他们授予对所有行的访问权限。 以下是对行级别安全性策略的查询。

let IsManager = current_principal_is_member_of('aadgroup=sales_managers@domain.com');
let AllData = Sales | where IsManager;
let PartialData = Sales | where not(IsManager) and (SalesPersonAadUser == current_principal()) | extend EmailAddress = "****";
union AllData, PartialData

向不同 Microsoft Entra 组的成员公开不同的数据

如果你有多个 Microsoft Entra 组,并且希望每个组的成员可以查看不同的数据子集,请将此结构用于 RLS 查询。

Customers
| where (current_principal_is_member_of('aadgroup=group1@domain.com') and <filtering specific for group1>) or
        (current_principal_is_member_of('aadgroup=group2@domain.com') and <filtering specific for group2>) or
        (current_principal_is_member_of('aadgroup=group3@domain.com') and <filtering specific for group3>)

在多个表上应用同一 RLS 函数

首先,定义一个函数,使该函数接收表名作为字符串参数,并使用 table() 运算符引用该表。

例如:

.create-or-alter function RLSForCustomersTables(TableName: string) {
    table(TableName)
    | ...
}

然后通过以下方式在多个表上配置 RLS:

.alter table Customers1 policy row_level_security enable "RLSForCustomersTables('Customers1')"
.alter table Customers2 policy row_level_security enable "RLSForCustomersTables('Customers2')"
.alter table Customers3 policy row_level_security enable "RLSForCustomersTables('Customers3')"

在进行未经授权的访问时生成错误

如果希望未经授权的表用户收到错误而不返回空表,请使用 assert() 函数。 以下示例演示如何在 RLS 函数中生成此错误:

.create-or-alter function RLSForCustomersTables() {
    MyTable
    | where assert(current_principal_is_member_of('aadgroup=mygroup@mycompany.com') == true, "You don't have access")
}

可以将此方法与其他示例组合在一起。 例如,可以向不同 Microsoft Entra 组中的用户展示不同的结果,并为所有其他人生成一个错误。

控制对后继数据库的权限

你在生产数据库上配置的 RLS 策略还将在随从数据库中生效。 不能在生产数据库和随从数据库上配置不同的 RLS 策略。 但是,你可以在 RLS 查询中使用 current_cluster_endpoint() 函数来达到与在后继表中使用不同 RLS 查询相同的效果。

例如:

.create-or-alter function RLSForCustomersTables() {
    let IsProductionCluster = current_cluster_endpoint() == "mycluster.chinaeast2.kusto.chinacloudapi.cn";
    let DataForProductionCluster = TempTable | where IsProductionCluster;
    let DataForFollowerClusters = TempTable | where not(IsProductionCluster) | extend EmailAddress = "****";
    union DataForProductionCluster, DataForFollowerClusters
}

注意

上述 RLS 函数对先导群集上的查询没有任何性能影响。 对后继群集上的查询的性能影响将仅受 DataForFollowerClusters 复杂性的影响。

更多用例

  • 呼叫中心支持人员通过身份证号的几个数字就可以辨识呼叫者。 不应向支持人员完全公开此号码。 可以在表上应用 RLS 策略以掩盖任意查询的结果集中身份证号除最后四位数以外的其他所有数字。
  • 设置对个人身份信息 (PII) 进行掩盖的 RLS 策略,使开发人员能够在不违反合规性法规的情况下,对生产环境进行查询,以便进行故障排除。
  • 医院可以设置 RLS 策略,以允许护士仅查看自己患者的数据行。
  • 银行可以设置 RLS 策略,以根据员工的业务部门或角色来限制对财务数据行的访问权限。
  • 多租户应用程序可以将来自多个租户的数据存储在单个表集中(这非常高效)。 它们将使用 RLS 策略来强制对每个租户的数据行与所有其他租户的行进行逻辑分离,使每个租户只能看到其自己的数据行。

对查询的性能影响

在表上启用 RLS 策略后,对访问该表的查询会产生一定的性能影响。 对该表的访问权限将被在该表上定义的 RLS 查询所取代。 RLS 查询的性能影响通常包括两部分:

  • Microsoft Entra ID 中的成员身份检查:检查效率高。 可以在数十个甚至数百个组中检查成员身份,而不会对查询性能产生重大影响。
  • 对数据应用的筛选器、联接和其他操作:影响取决于查询的复杂性

例如:

let IsRestrictedUser = current_principal_is_member_of('aadgroup=some_group@domain.com');
let AllData = MyTable | where not(IsRestrictedUser);
let PartialData = MyTable | where IsRestrictedUser and (...);
union AllData, PartialData

如果用户不属于 some_group@domain.com,则 IsRestrictedUser 被评估为 false。 评估的查询类似于以下查询:

let AllData = MyTable;           // the condition evaluates to `true`, so the filter is dropped
let PartialData = <empty table>; // the condition evaluates to `false`, so the whole expression is replaced with an empty table
union AllData, PartialData       // this will just return AllData, as PartialData is empty

同样,如果 IsRestrictedUser 的评估结果为 true,则只会评估对 PartialData 的查询。

提高使用 RLS 时的查询性能

对引入的性能影响

对引入没有任何性能影响。