PowerBI 企业级权限控制全动态终极解决方案
- 2019 年 10 月 6 日
- 笔记

任何在企业中的应用首先涉及的就是:权力。错了,是权限。因此权限控制是任何解决方案应该率先考虑的。本文给出在PowerBI中的动态权限控制的终极解决方案:用Excel配置权限后自动适应。
PowerBI 中的权限控制全面解析
PowerBI中的权限控制是分层次的,具体请以官方文档为准。但为了便于快速理解,这里特此编制了一个权限结构图:

这里涉及到8个构件,并用一个比喻来理解PowerBI的权限控制,想象一下:你作为自己家的主人,想让陌生人到你家的某个抽屉里拿东西。如下:
- Power BI Pro 账号,如:[email protected]。首先,应该有一个账号可以登录云端,因为Power BI的权限控制的最终体现是在云端的。这就像是一个居民身份证。即使是陌生人,也必须有身份证。
- Workspace(工作区),可以理解为你家。家里有两种角色,一种是管理员,一种是成员。你是自己家的管理员(Admin),管理员具有对家里无限的权限。管理员可以添加或改变成员。家里的成员对家里内容有两种可能的权限,可编辑和只读。可编辑家里内容的成员对家里的东西也有无限的修改权限。而只读的成员则只能看。
- 对于只能看的成员,看的内容就是数据集。如果这种成员是只能看的,可以进一步限制能看的范围。
- Row-Level Security(行级别安全性),确定了那种只能看的成员的可看的范围。并通过:角色-成员 与 模型-表格 的结构进行可能的多种控制。
- Role(角色),行级别安全性直接控制的是角色,而并不直接管理某成员。某个角色可以有多个成员。
- Member(成员),成员也可以有多个角色,在成员有多个角色的情况下,成员可以看到的内容,由多个角色共同决定。如:角色A可以看北京数据,角色B可以看上海数据,则具有角色A和角色B的成员可以看北京和上海的数据。
- Model(数据模型),由多个形成关系的表组成,权限的控制将按关系的箭头方向进行传递。但当涉及到含行级别安全性的双向传递时,可能引发复杂状况,稍后单独说明。
- Table(dim 或 fact),首先需要纠正一个概念,在PowerBI中是不存在维度表和事实表这种说法的,所有的表之间都是平等的关系,但也许有人(传统IT)习惯使用维度表或事实表的概念便于理解,但真实情况并非如此,没有DW概念的终端用户是不会事先知道维度表或事实表的(这超过了本文的范围,不再展开)。
因此,这里可以澄清几个重点了:
- 一上来就认为设置了行级别安全性就可以控制安全是错误的认识。
- 安全性的设置是分层次的。
- 行级别安全性的设置对双向筛选是有影响的。例如:只能看北京地区销售数据的经理,应该看到的客户数是全局的还是北京的。答案显然是北京的,但往往会由于忽略设置,导致看到的是所有的。
另外,如要使用行级别安全性的控制,首先必须满足如下条件:
- 被控制的用户,应该有Power BI Pro账号。
- 被控制的用户,应该在被控制的工作区,并且是只读的成员。(此步非常重要,且必须是只读的)
- 被控制的用户,应该添加进入某个数据集的安全性设置的某个角色。
接下来才轮到行级别安全性的控制来接管。
初步方案:PowerBI 中行级别安全性的静态设置
什么是静态设置,就是一旦设置了,就固定了。标准流程如下:
- 设置一个角色,如:北京大区
- 为角色设置筛选,如:地区[省份] = “北京”
重复上述过程,设置了很多管理区域,并在云端将不同地区的管理成员放入不同的大区角色。 这种静态的设置方法,最大的特点是:
- 简单
- 重复了很多类似的操作
- 没有用到 USERNAME() 函数
对于这种方案,由于很简单,可以在网上找到大量教程,不再介绍。总之:没有用 USERNAME() DAX 函数 的方案肯定是静态的,也一定不是最佳的。(当然,用了USERNAME() DAX 函数也不一定就是最佳的)
改良方案:用 USERNAME() 实现动态安全性设置
这种方法已经在此前的 PowerBI 实现不同角色看到内容不同支持动态权限管理 已经描述过,其核心在于:
- 通过 USERNAME() 来判断当前登录云端的用户账号
- 建立一个权限表
- 通过 DAX 表达式 来判断权限
该方案已经足够好用,但本文将做进一步改进,改进成全自动形式并且完全对用户透明的方法。
终极方案:由 Excel 权限控制表来动态控制 PowerBI 权限
这又是 OCP(开放闭合原则) 原则的充分体现,即:对扩展开放,对修改关闭。首先,先来体验下这是怎样的效果:
- 用户在Excel文件中,设置某些权限,完成,静待生效即可。
你在惊讶,这没PowerBI什么事啊,没错,确实没PowerBI什么事,因为复杂性全部被封装了,也是目前为止,PowerBI中能实现相对完美的不多的设计。下面来图解这个过程:

这个配置表在多次设计后,已经对操作用户做到了极致的友好:
- 用户名称和用户账号指定了被授予权限的最终读者。
- 后面的列可以任意扩展,注意:可以任意加入新列。
- 每列的单元格中可以指明该用户可以查看的内容,内容之间以任何分隔符分隔,如:分号,逗号,竖线等随你。
从这个配置表,用户可以非常容易理解他需要怎么做,为什么不用管PowerBI呢,因为PowerBI的下一次自动刷新会通过数据网关将该配置表重新读入,进而建立新的权限机制,因此,这实现了一种:增加或减少权限配置而不需要编辑PowerBI的文件的巧妙做法。这就诠释了OCP原则,对扩展开放,对修改关闭,这里开放的是用户可以任意扩展配置表的内容,而关闭的是对PowerBI文件的修改,所有的架构设计都应该遵循OCP原则。
下面来详细说明,PowerBI文件中都做了什么来实现这样的动态安全性控制。
首先,看下PowerBI文件的查询结构:

可以看出我们单独设置了权限控制部分的配置表和度量值,来看看权限控制表的实现:

这里对用户的配置,进行了逆透视以及规范化处理,形成了便于在PowerBI中便于使用DAX处理的结构,这个结构是通用的,可以:允许用户配置时任意增加透视列或单元中的项数,配置文件路径以及分隔符全部用参数给出,仍然满足OCP原则。如果要说这里算有难度的地方,就是如何把一个透视表,也单元格内含有多项内容的表格,自动转换为原始的规范化表,这由PowerBI的查询编辑完成。这里比较厚道地全部粘贴如下:
let Source = Excel.Workbook(File.Contents( 权限控制表路径 ), null, true), AccessControl_Table = Source{[Item="AccessControl",Kind="Table"]}[Data], #"Unpivoted Columns" = Table.UnpivotOtherColumns(AccessControl_Table, {"用户名称", "用户账号"}, "权限分类", "拥有权限"), #"Extracted First Characters" = Table.TransformColumns(#"Unpivoted Columns", {{"拥有权限", each List.RemoveItems( Text.Split( _ , 权限控制分隔符 ) , {""} ) }}), #"Expanded {0}" = Table.ExpandListColumn(#"Extracted First Characters", "拥有权限"), #"Changed Type" = Table.TransformColumnTypes(#"Expanded {0}",{{"拥有权限", type text}}), #"Trimmed Text" = Table.TransformColumns(#"Changed Type",{{"拥有权限", Text.Trim, type text}}) in #"Trimmed Text"
然后来到PowerBI数据模型中看看是用怎样的度量值来计算和控制权限的,我们根据需要实现了判断权限的度量值如下:


观察上述两个分别控制产品以及地区的度量值结构,只需要修改最前面两行的实体部分即可,其他部分不做任何改变。这样,当需要判断更多权限的情况产生时,只需要复制和修改这个DAX表达式即可。
这里其实是不满足DRY设计原则的,因为存在大量重复,这也是DAX作为编程方式的边界,它不真正在设计上支持工程化的形式。非常希望微软能在DAX表达式本身加入更多的动态特性。原理上,只需要改进编译器即可,并不是很难的问题,但以M语言加入智能提示的速度来看,不敢奢求了。
然后,在角色中这样控制权限:

由于这个方案已经将实际的权限控制全部交给配置表动态处理,其实已经不需要利用PowerBI的多角色机制了,只需要一个角色,随便起个名字即可,然后使用刚刚的度量值,分别对应要控制的表放入即可,如此简单。
然后就可以看看在Power BI Desktop中的模拟效果,如下:

可以注意到该用户 [email protected] 的权限完全被限制在和Excel配置文件中完全一致的范围。整套机制堪称完美。我们再到云端来看看实际效果:

用户楚杰被牢牢锁在只有只读权限并被控制了范围的状态下,对此,用户也毫无怨言。再来欣赏下,Excel配置文件是如何可以动态刷新的:


因此,终端用户永远只需要修改Excel文件即可,这种权限的控制,实现了完全和PowerBI本身解耦,是在PowerBI企业级权限控制上,目前为止所见的最佳方式。尤其可以应对大量用户(例如超过100人)的统一在Excel中配置其权限。
最后的补充:双向安全筛选器
在学习PowerBI建立关系时,很多人好奇一个地方:

这个在两个方向上应用安全筛选器是什么意思,在这里的场景下:

会出现两种理解:
- 由于客户表并没有受到权限控制,应该显示所有客户。
- 虽然客户表并没有受到权限控制,但由于产品表和地区表受到了权限控制,客户表应该被间接控制,由于客户表启用了双向筛选。
如果,用户的需求希望按 1 来,则不需要启用安全筛选,如果希望按 2 来,则需要启用安全筛选。来看看效果:
不启用安全筛选:

启用安全筛选:

这就为企业级安全的精细控制做出了非常准确的约束。
超越限制
如果可以按照本文的思路非常准确认真的实现配置化的动态权限控制,不难发现:
- 不需要PowerBI行级别安全性机制一眼可以控制权限
- 行级别安全性要求配置角色等构件元素,而用配置的方式可以超越这些限制
另外,某些现实的需求在行级别安全性下成了限制,例如:
- 某零售店铺只能查看到自己的数据
- 该零售店铺可以查看到全局所有零售店铺的平均值来做参考
在行级别安全性下,由于数据已经被筛选,因此无法实现上述需求,需要做一些补充才能完成。例如:再加载一次事实表,单独计算。当然,如果可以重复理解本文思路,也可以完全不使用行级别安全性来控制权限。读者可以自行试验,欢迎探讨。
总结
本文系统全面了描述了企业级全动态的安全性配置解决方案,并使用Excel文件从PowerBI中完全解耦,实现了终端用户只需要配置Excel即可完全复杂的权限配置。正文部分已经将思路和核心逻辑以及核心函数全部贴出,大家可以参考实现。另外,这里已经给出了超过行级别安全性控制的方法,进而可以实现更多的可能性,欢迎交流讨论。