Power BI 中的行级安全性:多租户数据访问
行级安全性 (RLS) 是一种确保每个用户只能看到他们有权访问的数据的机制。在多租户或多部门环境中,RLS 不是可选的——它是受监管的分析平台与即将发生的数据泄露之间的区别。然而,Microsoft 自己的使用情况遥测表明,只有不到 30% 的拥有 Power BI Premium 的组织在其生产数据集上实施了 RLS。
原因并不是 RLS 在概念上很困难。原因是实现细节非常微妙,测试过程是手动的,而且 RLS 和其他 Power BI 功能(DirectQuery、复合模型、嵌入、聚合)之间的交互会产生让团队措手不及的边缘情况。本指南涵盖了 RLS 实施的各个方面,从最简单的静态角色到与 Azure Active Directory 集成的复杂动态安全性。
要点
- Power BI 中的行级安全性使用 DAX 表达式在模型级筛选数据,确保用户无法通过修改报告或视觉效果来绕过安全性
- 静态 RLS 硬编码过滤器值(适合小型、稳定的用户组),而动态 RLS 使用 USERNAME() 和 USERPRINCIPALNAME() 等 DAX 函数根据登录用户进行动态过滤
- RLS 仅适用于导入和 DirectQuery 模式 --- 它不适用于与 Analysis Services 的实时连接(它有自己的 RLS)
- 对象级安全性 (OLS) 隐藏整个表或列,为用户甚至不应该知道某些数据存在的场景补充 RLS
- 测试 RLS 需要 Power BI Desktop 和服务中的“查看为”功能 --- 切勿在未对每个角色进行显式测试的情况下假设 RLS 可以正常工作
- 嵌入式场景(Power BI Embedded)中的 RLS 需要在嵌入令牌中传递有效身份,这是实现错误的常见来源
- 对于设计良好的模型,RLS 的性能影响通常小于 5%,但编写不当的 DAX 过滤器可能会使性能降低 50% 或更多
了解行级安全性
RLS 的作用
RLS 将 DAX 筛选表达式应用于 Power BI 数据模型中的一个或多个表。当用户打开报表时,Power BI 会评估 RLS 规则并以静默方式筛选出用户无权查看的行。用户体验到正常的报告——他们根本无法看到超出其范围的数据。
重要的是,RLS 在数据模型层运行,而不是在报告层。这意味着:
- 用户无法通过在同一数据集上创建自己的报告来绕过 RLS
- RLS 过滤器通过关系传播(Dim_Region 上的过滤器自动通过关系过滤 Fact_Sales)
- DAX 度量尊重 RLS 上下文(CALCULATE、SUMX 和其他函数对过滤的子集进行操作)
- 导出到 Excel、CSV 或 PowerPoint 仅导出用户有权查看的数据
RLS 与其他安全机制
| 机制 | 范围 | 执法 |
|---|---|---|
| 工作区访问 | 谁可以看到工作区 | Power BI 服务 |
| 应用程序权限 | 谁可以访问已发布的应用程序 | Power BI 服务 |
| 行级安全 | 用户看到哪些行 | 数据模型(DAX) |
| 对象级安全 | 用户看到哪些表/列 | 数据模型(元数据) |
| 敏感度标签 | 分类与保护 | 微软权限 |
| 数据导出限制 | 用户是否可以导出数据 | 报告/工作区设置 |
RLS 是控制用户可以访问哪些特定数据行的唯一机制。其他机制控制工作区、报表或对象级别的访问。
静态行级安全性
静态 RLS 使用硬编码的过滤器值将用户分配给角色。这是最简单的实现方式,适用于少数固定区域、部门或业务单元的场景。
创建静态角色
在 Power BI 桌面中:
- 转到建模,然后管理角色 2.点击创建添加新角色
- 命名角色(例如“北美销售”)
- 选择要过滤的表(例如 Dim_Region)
- 编写DAX过滤表达式:
[Region] = "North America"
该表达式的意思是:当用户被分配“北美销售”角色时,与 Dim_Region 相关的每个表将仅显示区域为北美的行。查看销售报告的用户只能看到北美销售情况。查看 HR 仪表板(如果通过区域维度连接)的用户将仅看到北美员工。
多重角色
您可以使用不同的过滤器创建多个角色:
- 欧洲、中东和非洲销售:
[Region] = "EMEA" - 亚太地区销售:
[Region] = "APAC" - 全球执行官:无过滤器(查看所有数据)
一个用户可以被分配多个角色。当分配给多个角色时,过滤器与 OR 逻辑相结合——用户看到所有角色数据的并集。例如,分配给“北美销售”和“欧洲、中东和非洲销售”的用户会看到这两个区域的数据。
静态 RLS 的局限性
在以下情况下,静态 RLS 变得难以管理:
- 您有超过 10-15 个不同的过滤器值(创建和维护 15 个以上的角色很乏味)
- 用户到角色的分配经常更改(每次更改都需要 Power BI 管理员)
- 过滤逻辑比简单的相等更复杂(例如,管理者应该看到他们团队的数据加上他们自己的数据)
- 您在数十个业务部门拥有数百名用户
对于这些场景,动态 RLS 是解决方案。
动态行级安全性
动态 RLS 使用在运行时评估的 DAX 函数来确定登录用户并应用适当的筛选器。两个关键功能是:
- USERNAME() — 返回当前用户的域\用户名或 UPN
- USERPRINCIPALNAME() — 返回当前用户的电子邮件/UPN(建议用于云部署)
设置动态 RLS
第一步:创建安全映射表
该表将用户映射到其授权数据范围。它可以存储在数据源(数据库)、SharePoint 列表或 Excel 文件中:
| 用户邮箱 | 地区 | 部门 | 公司编号 |
|---|---|---|---|
| [email protected] | 北美 | 销售 | 1 |
| [email protected] | 欧洲、中东和非洲 | 销售 | 2 |
| [email protected] | 亚太地区 | 运营 | 3 |
| 卡罗尔@company.com | 全部 | 全部 | 全部 |
将此表作为 SecurityMapping 导入到 Power BI 模型中。
步骤 2:创建 RLS 角色
在安全映射表上使用 DAX 过滤器创建单个角色(例如“DynamicSecurity”):
[UserEmail] = USERPRINCIPALNAME()
|| [UserEmail] = "ALL"
第 3 步:建立关系
建立从 SecurityMapping 到维度表的关系:
- SecurityMapping[区域] 到 Dim_Region[区域]
- SecurityMapping[部门] 到 Dim_Department[部门]
- SecurityMapping[CompanyId] 到 Dim_Company[CompanyId]
这些关系从维度到安全映射表必须是一对多,或者您可以使用双向交叉过滤器。然而,双向过滤器对性能有影响——更好的方法是在 DAX 表达式中使用 CROSSFILTER 或 TREATAS。
步骤 4:没有关系的替代方案(TREATAS 方法)
您可以直接在事实表的 RLS 表达式中使用 TREATAS,而不是从安全映射表创建关系:
VAR CurrentUser = USERPRINCIPALNAME()
VAR UserRegions =
CALCULATETABLE(
VALUES(SecurityMapping[Region]),
SecurityMapping[UserEmail] = CurrentUser
|| SecurityMapping[UserEmail] = "ALL"
)
RETURN
[Region] IN UserRegions
这种方法避免了额外关系的复杂性并保持安全逻辑的独立性。
具有管理器层次结构的动态 RLS
一个常见的要求是管理者可以查看整个报告链的数据。这需要员工或用户表中的父子层次结构。
方法一:PATH函数
如果您的用户表有 ManagerId 列,请使用 DAX 的 PATH 函数:
UserPath = PATH(Users[UserId], Users[ManagerId])
那么在RLS表达式中:
VAR CurrentUserId =
LOOKUPVALUE(Users[UserId], Users[Email], USERPRINCIPALNAME())
RETURN
PATHCONTAINS([UserPath], CurrentUserId)
对于当前用户自己的数据以及属于其直接和间接报告的所有数据,此表达式返回 TRUE。
方法 2:扁平化安全表
预先计算 ETL 流程中的层次结构并创建平面安全映射,其中列出每个经理及其所有报告的数据范围。这在查询时性能更高,因为它避免了 PATH 评估的开销。
对象级安全性 (OLS)
对象级安全性向用户隐藏整个表或列。与过滤行的 RLS 不同,OLS 使表或列完全不可见——它们不会出现在字段列表中,并且任何引用隐藏字段的视觉效果都会显示错误。
何时使用 OLS
- 对非 HR 用户隐藏 HR 数据集中的薪资列
- 对只应看到收入的销售团队隐藏与成本相关的表格
- 对只需要汇总数据的分析师隐藏客户 PII(电子邮件、电话、地址)
- 对一般用户隐藏战略定价列
配置 OLS
OLS 通过表格编辑器(外部工具)或 XMLA 终结点进行配置,而不是通过 Power BI Desktop UI。
在表格编辑器中:
1.通过外部工具功能区打开模型 2. 导航到要限制的表或列 3. 在属性窗格中,找到每个角色下的表权限或列权限 4.将权限设置为“无”(默认为“读取”)
例如,要对“销售”角色隐藏员工表中的薪资列:
- 角色:销售
- 表:员工
- 栏目:薪资
- 权限:无
分配了销售角色的用户将看不到字段列表中的“薪资”列,也无法在 DAX 计算中引用它。
OLS 限制
- OLS 需要启用 XMLA 端点的 Power BI Premium 或 Pro
- 无法在 Power BI Desktop 的本机 UI 中配置 OLS
- OLS 仅是元数据级别 --- 它不过滤行
- 如果某个度量引用了隐藏列,则该度量本身将对受限用户出错
RLS 与 DirectQuery
RLS 可与 DirectQuery 配合使用,但其行为在重要方面与导入模式不同。
它是如何运作的
在 DirectQuery 模式下,Power BI 将 RLS DAX 筛选器转换为 SQL WHERE 子句并将其发送到数据源。数据源执行过滤,仅返回授权行。
单点登录 (SSO) 传递
将 DirectQuery 与 SSO 结合使用到 Azure SQL 或 Azure Synapse 等数据库时,Power BI 会将用户的身份传递到数据库。如果数据库有自己的行级安全性(例如 SQL Server 的 CREATE SECURITY POLICY),则除了 Power BI 的 RLS 之外,该安全性也适用。
重要提示: 如果启用 SSO 传递,Power BI 的 RLS 将被绕过,因为数据库会处理安全性。您必须选择其中之一:
- Power BI RLS(在 DAX 中定义,在 Power BI 中管理)
- 数据库级RLS(在SQL中定义,在数据库中管理)
- 两者(Power BI RLS 和数据库 RLS 应用 --- 用户看到交集)
性能考虑因素
DirectQuery 中的 RLS 筛选器向每个查询添加 WHERE 子句。如果过滤器列未在数据库中建立索引,性能可能会显着降低。确保:
- RLS过滤列有数据库索引
- DAX 过滤表达式足够简单,可以转换为高效的 SQL
- 使用 Power BI Desktop 中的“性能分析器”测试查询性能
Power BI Embedded 中的 RLS
Power BI Embedded(在自定义应用程序中嵌入报表)具有独特的 RLS 要求,因为最终用户可能没有 Power BI 或 Azure AD 帐户。
应用程序拥有数据场景
在“应用程序拥有数据”嵌入模式中,服务主体或主帐户向 Power BI 进行身份验证,并且应用程序在嵌入令牌中传递用户身份。
使用 RLS 生成嵌入令牌:
调用 Power BI REST API 生成嵌入令牌时,请包含 identities 参数:
{
"datasets": [
{
"id": "dataset-guid-here"
}
],
"reports": [
{
"id": "report-guid-here"
}
],
"identities": [
{
"username": "[email protected]",
"roles": ["DynamicSecurity"],
"datasets": ["dataset-guid-here"]
}
]
}
username 值是 USERPRINCIPALNAME() 在 DAX 表达式中返回的值。 roles 数组指定要应用的 RLS 角色。您可以传递任何字符串作为用户名 --- 它不需要是真正的 Azure AD 帐户。
常见嵌入错误
错误 1:未传递有效身份。 如果生成不带 identities 参数的嵌入令牌,嵌入报告将显示所有数据。这是最常见的 RLS 嵌入错误。
错误 2:传递角色但不传递用户名。 动态 RLS 需要用户名。如果没有它,USERPRINCIPALNAME() 将返回空白,并且 DAX 过滤器不会匹配任何行 --- 报表显示为空。
错误 3:使用服务主体的身份。 服务主体是工作区管理员并绕过 RLS。您必须显式传递最终用户的身份。
错误 4:在动态 RLS 的嵌入令牌中对角色进行硬编码。 如果您将动态 RLS 与单个角色(例如“DynamicSecurity”)一起使用,请始终传递该角色名称。不要为每个用户创建单独的角色 --- 这违背了动态 RLS 的目的。
测试行级安全性
查看角色 (Power BI Desktop)
在 Power BI Desktop 中,转到“建模”,然后“查看为”:
- 选择要测试的角色
- (可选)输入用户名来测试动态 RLS(这会模拟 USERPRINCIPALNAME() 值)
- 单击“确定”
该报告现在显示数据,就好像您是具有指定角色的指定用户一样。验证:
- KPI 卡显示正确的过滤总计
- 表格仅显示用户范围内的行
- 图表反映过滤后的数据
- 视觉效果之间的交叉过滤尊重 RLS 边界
- 钻取页面维护 RLS 上下文
查看方式(Power BI 服务)
在 Power BI 服务中,打开数据集设置并选择安全性。您可以通过选择“测试角色”并输入用户名来直接测试角色。
自动化测试清单
创建包含以下场景的测试矩阵:
| 测试用例 | 预期结果 |
|---|---|
| 具有单一角色的用户 | 仅查看其地区/部门/公司数据 |
| 具有多个角色的用户 | 查看所有分配角色数据的并集 |
| 未分配角色的用户 | 没有看到任何数据(报告为空) |
| 具有 ALL/全局访问权限的用户 | 查看所有数据 |
| 具有层级访问权限的经理 | 查看自己的数据以及所有直接/间接报告 |
| 新维度增值 | 验证新值是否对适当的用户可见 |
| 导出到 Excel | 导出的数据遵循 RLS 过滤器 |
| 订阅电子邮件 | 电子邮件包含经过 RLS 过滤的数据 |
| 自然语言问答 | 答案尊重 RLS 过滤器 |
| 移动应用程序 | RLS 适用于移动视图 |
RLS 性能优化
衡量影响
在实施 RLS 之前和之后,使用 Power BI Desktop 中的性能分析器来测量查询时间。打开性能分析器窗格,开始记录,与报告交互,并比较使用和不使用 RLS 的 DAX 查询时间。
设计良好的 RLS 实施增加的开销不到 5%。如果您发现性能下降超过 10%,请调查 DAX 筛选器表达式。
优化技术
保持过滤表达式简单。 理想的 RLS 表达式是单列比较:
[Region] = USERPRINCIPALNAME()
避免在 RLS 过滤器本身中使用多个 CALCULATE、FILTER 或 LOOKUPVALUE 调用来实现复杂表达式。
使用整数键而不是文本比较。 对 [CompanyId] = 1 进行过滤比 [CompanyName] = "ECOSIRE Private Limited" 更快。将用户电子邮件映射到安全映射表中的整数键。
使用 RLS 过滤器最小化表的数量。 将 RLS 应用于维度表并让关系传播处理事实表。将 RLS 直接应用于大型事实表会强制 Power BI 对事实表的每一行评估筛选器。
尽可能进行预聚合。 如果用户只需要摘要级别的数据,请考虑创建一个预聚合表,并在 ETL 期间应用安全过滤器。这减少了 Power BI 在查询时需要筛选的数据量。
**避免双向交叉过滤器。**双向关系会增加查询复杂性并可能与 RLS 发生冲突。使用单向关系(从维度到事实)并在维度侧应用 RLS。
常见陷阱和解决方案
陷阱 1:RLS 不适用于工作区管理员
具有编辑权限的工作区管理员和成员绕过 RLS。他们总是看到所有数据。这是设计使然——管理员需要完全访问权限才能管理工作区。
解决方案: 对应受 RLS 约束的业务用户使用“查看者”角色。仅向 BI 团队授予管理员/成员/贡献者角色。
陷阱 2:ALL() 删除 RLS 过滤器
DAX ALL() 函数从表中删除所有筛选器,包括 RLS 筛选器。如果度量在经过 RLS 过滤的表上使用 ALL(),则可能会暴露用户不应看到的数据。
-- DANGEROUS: This measure ignores RLS on Dim_Region
Total Global Sales =
CALCULATE(SUM(Fact_Sales[Revenue]), ALL(Dim_Region))
解决方案: 当您想要删除切片器/视觉过滤器但保留 RLS 过滤器时,请使用 ALLSELECTED() 而不是 ALL():
-- SAFE: This measure preserves RLS filters
Total Sales for Context =
CALCULATE(SUM(Fact_Sales[Revenue]), ALLSELECTED(Dim_Region))
陷阱 3:计算覆盖 RLS
在某些情况下,使用显式过滤器参数的 CALCULATE 可以覆盖 RLS,特别是使用 REMOVEFILTERS:
-- DANGEROUS: REMOVEFILTERS is equivalent to ALL
Total Revenue All Regions =
CALCULATE(SUM(Fact_Sales[Revenue]), REMOVEFILTERS(Dim_Region[Region]))
解决方案: 审核 ALL、REMOVEFILTERS 和 ALLEXCEPT 使用情况的所有 DAX 度量。确保它们不引用 RLS 过滤列。
陷阱 4:复合模型和 RLS
在复合模型(混合导入和 DirectQuery)中,必须为导入表和 DirectQuery 表单独定义 RLS。单个 RLS 角色可以包含两者的过滤器,但行为不同:
- 导入表:RLS 过滤器由 Power BI 引擎评估
- DirectQuery 表:RLS 过滤器转换为 SQL 并发送到源
如果 DirectQuery 源不支持 RLS 筛选器中使用的 DAX 函数,查询将失败。
陷阱 5:分页报告忽略 RLS
如果 Power BI 分页报表(在报表生成器中创建)直接连接到数据源,则可以绕过数据集 RLS。要强制实施 RLS,分页报表必须通过 Power BI 数据集连接(而不是直接连接到数据库),并且用户必须具有分配的 RLS 角色。
企业RLS架构模式
对于大型组织,ECOSIRE 建议 标准化 RLS 架构:
安全层设计
- 安全映射表 存储在中央数据库中(Azure SQL 或 SharePoint 列表)
- 单个 RLS 角色 使用 USERPRINCIPALNAME() 命名为“DynamicSecurity”
- Azure AD 组同步 根据组成员身份自动填充安全映射表
- 使用预展平父子表的层次结构支持
- 审核跟踪 记录哪些用户访问了哪些数据(通过 Power BI 活动日志和 REST API)
治理流程
- 数据管理员维护安全映射表
- 通过变更管理流程审核和批准变更
- 每月审核将 Power BI RLS 分配与 HR 记录系统进行比较
- 每季度渗透测试验证 RLS 有效性
该架构可扩展到数百个数据集的数千个用户,同时保持单点安全管理。
常见问题解答
RLS 是否可以与 Power BI 免费许可证一起使用?
不需要。对于使用 RLS 保护的报表的所有用户,RLS 需要 Power BI Pro 或 Premium Per User 许可证。免费许可证用户只能访问高级容量工作区中的内容,即使如此,他们也需要 Pro 或 PPU 许可证才能分配 RLS 角色。在 Power BI Embedded 场景中,最终用户不需要 Power BI 许可证 --- RLS 通过嵌入令牌强制执行。
我可以基于 Azure AD 组而不是个人用户实施 RLS 吗?
不直接。 Power BI 的 RLS 根据 USERPRINCIPALNAME() 评估 DAX 表达式,该表达式返回单个用户的电子邮件。但是,您可以创建一个安全映射表,将 Azure AD 组映射到数据范围,并使用 Microsoft Graph API 或 Azure AD 组成员身份同步来填充该表。 DAX 表达式仍然按用户的电子邮件进行过滤,但安全映射表提供组到数据的映射。
如果用户未分配任何 RLS 角色,会发生什么情况?
如果在数据集上定义 RLS 并且用户未分配任何角色,则用户看不到任何数据。报告已加载,但所有视觉效果均显示空白或零。这是安全默认设置 --- Power BI 假定没有访问权限,除非明确授予。但是,具有“编辑”权限的工作区管理员和成员可以绕过 RLS,并且始终可以查看所有数据,无论角色分配如何。
RLS可以过滤实时仪表板中的数据吗?
是的。 RLS 适用于导入和 DirectQuery 模式。在 DirectQuery 模式下,RLS 筛选器会转换为 SQL WHERE 子句,并随每个查询一起发送到数据库,因此筛选是实时发生的。在导入模式下,当用户打开报表时,过滤将在内存中应用。两种模式提供相同的安全保证——用户只能看到授权的数据。
如何审核谁通过 RLS 访问了哪些数据?
Power BI 通过 Microsoft 365 管理中心和 Power BI REST API 提供活动日志。这些日志记录报告视图、数据集刷新和导出操作,包括用户的身份。但是,日志不会记录用户查看了哪些特定行。对于详细的数据访问审核,请启用数据库级审核(例如 PostgreSQL pgaudit 或 Azure SQL 审核)以记录应用了 RLS 筛选器的 DirectQuery 生成的实际查询。
作者
ECOSIRE Research and Development Team
在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。
相关文章
Power BI AI 功能:Copilot、AutoML 和预测分析
掌握 Power BI AI 功能,包括用于自然语言报告的 Copilot、用于预测的 AutoML、异常检测和智能叙述。许可指南。
Power BI 仪表板开发完整指南
了解如何使用 KPI 设计、可视化最佳实践、钻取页面、书签、移动布局和 RLS 安全性来构建有效的 Power BI 仪表板。
Power BI 数据建模:商业智能的星型架构设计
通过星型模式设计、事实和维度表、DAX 度量、计算组、时间智能和复合模型掌握 Power BI 数据建模。