在Power BI中开发和测试RLS规则

很多时候,并不是所有的用户都应该有权限访问报告中的所有数据。在这里,我将解释如何开发Power BI中的RLS规则来配置访问权限以及如何测试它们。

Photo by FLY:D on Unsplash

介绍

我的许多客户都希望基于特定规则限制其报告中的数据访问权限。

访问数据称为行级安全性(简称RLS)。

你可以在小猪AI上找到许多关于Power BI中RLS的文章。

我在下面的参考资料中添加了其中的两篇。

虽然所有文章都很好地解释了基础知识,但我总是缺乏如何开发更复杂的规则以及如何轻松测试它们的解释。

在本文中,我将解释RLS的基础知识并逐步添加复杂性。

此外,我还将向你展示如何使用DAX Studio构建查询以在将其添加到数据模型之前测试RLS规则。

所以,我们开始吧。

场景

我使用场景,即基于店铺或公司内店铺的地理位置,包括两者的组合,用户可以访问零售销售数据。

在Contoso数据模型中,我使用以下表:

Figure 1 — Tables involved in my scenario (Figure by the Author)

我创建以下报告以测试我的结果:

Figure 2 — Starting report (Figure by the Author)

创建一个简单的规则

要创建RLS规则,你需要打开安全角色编辑器:

Figure 3 — Open the Security role editor (Figure by the Author)

接下来,你可以创建一个新角色并设置此角色的名称:

Figure 4 — Create a Role and rename it (Figure by the Author)

在我的情况下,我将名称设置为“StorePermissions”。

现在,我可以开始添加表达式来控制对Store表的访问:

Figure 5 — Add a DAX expression to the new Role (Figure by the Author)

我们已经有了一个新的、更简单的RLS规则编辑器。

在我的情况下,我想添加一个DAX表达式。所以我点击“切换到DAX编辑器”按钮。

首先,我添加最简单的表达式:TRUE()

图6 — 最简单的RLS规则(作者提供的图)

要理解RLS规则,您必须知道访问是由RLS规则编辑器中表达式的输出控制的。

如果表达式的输出不为空或FALSE(),则用户将获得访问权限。

原则上,RLS规则编辑器中的任何表达式都会被添加为任何查询的过滤器。在我更详细地解释之前,让我们先看一下这个第一个表达式的效果。

为了测试该规则,我保存表达式并关闭编辑器。

现在我可以使用新规则查看报告:

图7 — 测试RLS规则(作者提供的图)

在报告页面顶部,您将看到一个黄色横幅,显示您正在使用StorePermission规则查看报告。

由于StorePermission规则不限制访问,因此您不会看到任何差异。

让我们尝试些不同的东西。

现在我将RLS规则中的表达式更改为FALSE()。

当我测试该规则时,我将不会看到任何数据:

图8 — 使用FALSE()测试规则(作者提供的图)

这证明如果表达式不返回FALSE(),则可以访问数据。

测试查询

要详细了解此效果,请让我展示一个DAX查询,以获取不受任何限制的结果:

EVALUATE  SUMMARIZECOLUMNS(          Store[Store]          ,"Retail_Sales", 'All Measures'[Retail Sales]          )ORDER BY Store[Store]

当我添加一个如上所示的TRUE()的RLS规则时,查询会变成类似于以下查询:

EVALUATE  FILTER(      SUMMARIZECOLUMNS(            Store[Store]            ,"Retail_Sales", 'All Measures'[Retail Sales]            )      ,TRUE()      )ORDER BY Store[Store]

我将查询包含在FILTER()函数中,并将TRUE()作为过滤器表达式添加。

在接下来的示例中,我将使用CALCULATETABLE(),因为编写代码更有效和灵活。

稍后再详细说明。

让它变得更加复杂

接下来,我想限制访问所有包含“Contoso T”字符串的商店。

为此,我将规则编辑器中的表达式更改为以下内容:

CONTAINSSTRING('Store'[Store], "Contoso T")

测试规则时,我会得到以下结果:

图9 — 限制访问“Contoso T”商店的结果(作者提供的图)

使用DAX查询测试规则

使用DAX Studio中的以下查询检查结果会很好:

EVALUATE  CALCULATETABLE(    SUMMARIZECOLUMNS(          Store[Store]          ,"Retail_Sales", 'All Measures'[Retail Sales]          )    CONTAINSSTRING('Store'[Store], "Contoso T") = TRUE()    )ORDER BY Store[Store]

内部部分使用SUMMARIZECOLUMNS()生成输出表格。

在本例中,我只对商店列表感兴趣。

然后,我使用CALCULATETABLE()将SUMMARIZECOLUMNS()调用封装起来,以添加查询过滤器。

在本例中,我添加了RLS规则中的表达式,包括“= TRUE()”检查。

结果如下:

Figure 10 — Result of check query (Figure by the Author)

但是,在幕后会发生什么?

让我们看看存储引擎查询:

Figure 11 — Result of the check Query (Figure by the Author)

当我将RLS规则应用于此查询时会发生什么?

我可以使用DAX Studio中的几个点击应用RLS规则:

Figure 12 — Activate an RLS rule (Figure by the Author)

存储引擎查询如下:

Figure 13 — Query Analysis with the RLS Rule

第一个查询(第2行)检索所有商店的列表。

第二个查询将RLS规则包含在WHERE子句中。

我们不再看到匹配商店列表(根据过滤器),而是看到一个包含RLS规则的加密行。

您可以看到存储引擎(SE)查询的结果仍然包含309行,就像上面一样,这是所有商店的数量+ 3行。我们为什么有3行差异的提示在SE查询下面的文本中:估计大小:行= 309

实际返回的行数可能确实为306。

但是,该分析表明RLS规则在存储引擎之后应用,因为查询结果仅包含21行:以“Contoso T”开头的所有商店。

这很重要,因为公式引擎(FE)在存储引擎之后计算最终结果,并且是单线程的,只能使用一个CPU核心。

而SE是多线程的,可以使用多个CPU核心。

因此,我们必须避免在RLS规则中编写低效的代码。

组合规则

接下来,我想组合两个表达式:

  1. 仅以“Contoso T”开头的商店
  2. 仅在欧洲的商店

为此,我使用简单编辑器向地理表添加第二个表达式:

Figure 14 — Add expression to the Geography table (Figure by the Author)

当我切换到DAX编辑器时,我得到以下表达式:

Figure 15 — DAX Expression from the Simple editor (Figure by the Author)

注意使用严格相等运算符。

更改表达式的简单等号运算符可能是必要的。

这是测试规则时的结果:

Figure 16 — Result of the combined rule (Figure by the Author)

此规则的DAX查询如下:

Figure 17 — Translation to a DAX query and results (Figure by the Author)

现在,让我们给RLS规则增加另一层复杂性:

我想限制访问以下商店:

  • 名称以“Contoso T”开头且在欧洲的商店或
  • 名称以“Contoso S”开头并在北美洲的商店

这次,我从DAX查询开始。这是开发和测试表达式的简单方法。

我使用过滤表达式取第一个查询并将其括在其中。

由于我需要过滤两个表(Store和Geography),因此必须使用FILTER()和RELATED():

EVALUATE  CALCULATETABLE(    ADDCOLUMNS(      SUMMARIZECOLUMNS(Store[Store], 'Geography'[Continent])            ,"Retail_Sales", 'All Measures'[Retail Sales]            )    ,FILTER(Store        ,OR(CONTAINSSTRING('Store'[Store], "Contoso T") && RELATED(Geography[Continent]) = "Europe"          ,CONTAINSSTRING('Store'[Store], "Contoso S") && RELATED(Geography[Continent]) = "North America")        )    )ORDER BY [Retail Sales] DESC, 'Geography'[Continent], Store[Store]

由于我使用FILTER()迭代Store表,因此需要RELATED()函数,并且需要来自Geography表的Continent列。

由于Geography表位于关系的一侧,因此我可以使用RELATED()获取Continent列。

这是结果:

Figure 18 — Query for the combined Rule (Figure by the Author)

接下来,我们必须将此过滤器转换为RLS规则。

对于RLS规则,我们可以删除FILTER()函数,因为RLS规则本质上作为过滤器工作。

Figure 19 — Translation to one RLS Rule (Figure by the Author)

请注意,我已从“地理”表中删除了表达式。

当我在Power BI中测试此规则时,我得到了以下结果,这与DAX查询的结果相对应:

Figure 20 — Testing the combined RLS rule (Figure by the Author)

为了测试RLS规则,例如当您仅想获取经过过滤的商店列表时,您可以编写只有FILTER()函数的简单查询:

Figure 21 — Executing the FILTER() only (Figure by the Author)

基于用户登录配置访问权限

到目前为止,我们已经了解了静态的RLS规则。

但是大多数情况下,我们需要基于用户登录的规则。

为了实现这一点,我们需要一个表来映射用户到用户需要访问的行。

例如,像这样的表:

Figure 22 — User List with assigned Geographies (Figure by the Author)

将该表添加到数据模型后,我们需要在新表和“地理位置”表之间添加关系:

Figure 23 — Expanded data model (Figure by the Author)

新的“地理位置访问”表与“地理位置”表之间的关系必须正确配置。

添加关系后,Power BI将其配置为1:n关系,其中一侧为“地理位置”表,过滤器从“地理位置”表流向“地理位置访问”。

但是,我们希望基于“地理位置访问”的RLS规则(过滤器)过滤“地理位置”表。出于这个原因,我们必须将交叉筛选方向更改为“both”:

Figure 24 — Settings of the Relationship (Figure by the Author)

此外,我们必须在“两个方向上应用安全筛选器”上设置标志,因为Power BI在应用RLS规则时忽略交叉筛选方向设置。

现在我们可以添加RLS规则:

Figure 25 — Configure the RLS Rule (Figure by the Author)

添加此规则之前,请记得删除Store表上的任何过滤表达式。

测试RLS规则时,我得到了这个:

Figure 26 — Empty result (Figure by the Author)

为了找出发生了什么,请返回RLS规则编辑器,并将规则的视图更改为DAX:

Figure 27 — Wrong RLS rule (Figure by the Author)

简单的RLS规则编辑器无法识别DAX函数,并将其作为文本添加到过滤器中。

我们必须将表达式更改为:

Figure 28 — Correct DAX rule (Figure by the Author)

现在结果与预期相同:

Figure 29 — Testing the RLS rule with my user and the correct RLS expression (Figure by the Author)

报告页面左上角的卡片包含一个使用USERPRINCIPALNAME()函数确保正确用户在测试期间处于活动状态的度量。

我甚至可以使用另一个用户测试RLS规则:

图30-使用另一个用户测试RLS规则(作者提供的图)

有趣的是,这个用户不需要存在,只需要包含在“地理访问”列表中。

这是测试的结果:

图31-测试用户的测试结果(作者提供的图)

在顶部的黄色行中,您可以看到测试期间的活动用户。

结论

我向您展示了如何创建基本的RLS规则以及如何测试它们。

然后我增加了更多的复杂性,分析了RLS规则对底层存储引擎的影响。

我们已经看到公式引擎处理RLS规则的一部分。因此,我们必须在RLS规则中编写高效的代码。

在将RLS规则实施到数据模型之前,了解如何测试RLS规则非常重要。

通过了解规则如何应用于数据模型,更容易理解错误的结果。

最后,我向模型添加了动态的基于用户的RLS规则。

这些规则在DAX查询中更难测试,因为您必须知道每个用户可以访问哪些数据,以编写正确的测试查询以验证结果。

我希望我已经为您提供了一些关于在Power BI中使用RLS功能简化生活的提示。

Andrew George在Unsplash上的照片

参考资料

您可以在本文中找到Power BI中的安全功能列表:

Power BI中的4 + 2个安全功能

在此主题上发表第一篇文章一年后,更新了Power BI中的新安全功能

towardsdatascience.com

您可以在Power BI(现在是Fabric)社区页面上找到有关Power BI中行级安全性的简单说明:Row-level security (RLS) with Power BI — Power BI | Microsoft Learn。

我可以推荐Nikola Ilic撰写的这篇文章,其中您可以了解有关RLS的起点:

Power BI中行级和对象级安全性的终极指南

“报告中谁看到什么?”是Power BI中的关键安全问题之一。学习两种可能的实现方法……

towardsdatascience.com

Elias Nordlinder撰写的另一篇有关Power BI中行级安全性的入门文章:

如何在Power BI中实现行级安全性(第I部分)

行级安全性是一种根据不同角色以不同方式过滤数据的方法。这可能是静态完成的……

elias-nordlinder.medium.com

访问我的故事列表,了解有关FILTER()函数以及如何使用DAX Studio分析DAX查询的更多信息。

我使用了像我之前的文章中一样的Contoso样本数据集。您可以从Microsoft这里免费下载ContosoRetailDW数据集。

Contoso数据可在MIT许可下自由使用,如此处所述。

作为小猪AI会员,您的会员费的一部分将用于支付您所阅读的作家,并且您将获得对每个故事的完全访问权限…

小猪AI.com