Power plantform

Power BI企业级权限控制+页面控制部署终极方案

2021-07-18  本文已影响0人  大鹏_Power

这篇文章筹备了很久,最近正好有一个合适的契机,终于可以和大家见面了。Power BI在权限控制和页面控制方面,对于大多数人而言并不是一个很好用的功能,结合自己的经历,准备直接给大家分享终极方案。

内容过多,有些细节可能照顾不到,有疑问的可以留言,我会迭代上去。
导航栏效果

背景

20年我开始搭建企业级的Power BI模型。和以前作为分析师以个人分析为目的并不相同,企业级的部署需要考虑的内容很多,我选择了使用Power BI报表服务器的部署方案。

首先面临的问题就是如何给用户做权限控制,如何让用户使用Power BI报表像使用网页一样,让有权限的人看到应该看到的报表页面。

在Power BI原有的功能中,虽然可以做行级别权限控制,但不够灵活。对于页面控制+权限控制更是无力。尤其是对于初入Power BI的新手,权限控制简直是个灾难。

后来经过不断的尝试和反复的摸索学习,最终找到了一套简单的维护方案。

方案的主体部分,参考了佐罗老师《BI真经》高级版的内容。

什么情况下使用该方案?

该权限控制方案,适合报表数量非常多,比如100+,权限控制要求非常复杂,比如使用几十个字段甚至更多字段进行权限控制,那么这个方案就是你要找的最好的方案。如果你仅仅只有几个账号,权限相对稳定,那么这个方案可能并没有那么优秀。

各种Power BI部署方案与该权限控制方案的配合

Power BI Rerpot Server

创建域账号以后,将用户姓名、用户账号及对应的权限维护到权限配置表。然后按照后面的方法配置,就可以实现权限控制和页面控制了。

Power BI Pro

SSAS + Power BI

Power BI Preminum

和Power BI Pro雷同,但没有亲测过。

总结

Power BI权限控制流程

整个权限控制是由两个大的部分构成:

本次重点讲的是第二个部分,第一个部分目前我尚未整理出更好的方法,这部分是需要有相关开发能力作为支持才可以完成。

进入正题

思路讲解

技术拆解

如何制作配置表

配置表在Excel中完成,简单方便快捷,若定位于实现系统自动化,另当别论。配置完成后Power BI直接读取最新记录。需要三张Excel配置表,用于定义,页面配置用户权限角色权限,加载到模型里面的是页面配置用户权限两张表。

Excel配置表中的数据从何而来?

对于域账号,可以根据入职或离职的员工信息,自动创建域账号,同时保持和OA的账号密码一致,实现单点登录,然后再将该域账号写入到权限表,这么Power BI的权限控制就可以实现全自动了。

页面配置

页面配置表,需要几个关键的字段,其他的根据自己的实际需求添加,参考了佐罗老师的方案,做了一些调整。

该配置表为“”,相关引用使用的引用,而非单元格,可以减少数据调整导致的错误。不了解的请补充下关于“”的知识后再继续。

页面权限表可以无限向下或向右拓展

自然序号  = IF( [@部门名称] <> "", ROW()-ROW(页面配置表[#标题]), "" )
内容序号  = IF( [@自然序号] <> "", [@自然序号] - COUNTBLANK( OFFSET( 页面配置表[[#标题],[自然序号]], 0, 0, ROW( ) - ROW( 页面配置表[[#标题],[自然序号]] ) ) ), "" )
部门序号  = IF( [@部门名称] <> "", COUNTBLANK( OFFSET( 页面配置表[[#标题],[自然序号]], 0, 0, ROW( ) - ROW( 页面配置表[[#标题],[自然序号]] ) ) ), "" )
页面序号  = IF( [@部门名称] <> "", [@自然序号] - MINIFS( [自然序号], [部门名称], [@部门名称] ), "" )
部门编号  = IF( [@部门名称] <> "", [@部门序号]*1000+[@页面序号], "" )
部门名称  公司部门名称
页面描述  页面功能的描述
是否启用  是否启用对应的报表页
页面标题  报表页的名称
报表标题  报表页中,用于自动显示报表标题
......
页面配置表
角色权限

当用户特别多时,就需要使用角色进行配置,可以减少维护成本。相关内容以“;”做间隔。

角色权限表可以无限向右和向下拓展

角色编号  使用自然顺序创建角色编号
角色名称  增加角色的名称,自定义设置
快速配置  个人增加的一个快速配置页面权限的功能
页面权限  对应角色拥有的页面权限
城市      根据城市字段做权限控制
产品类别  根据产品类别做权限控制
......
角色配置

角色配置表中,页面权限部分,我做了两个中间表,可以快速方便的维护角色拥有的页面权限,当报表页面特别多时,该方式可以减少维护成本。该方式支持开通指定的页面。

该部分通过Power Query完成。

let
    源 = Excel.CurrentWorkbook(){[Name="页面配置表"]}[Content],
    筛选的行 = Table.SelectRows(源, each ([是否启用] = 1)),
    删除的其他列 = Table.SelectColumns(筛选的行,{"部门序号", "页面编号", "部门名称"}),
    更改的类型 = Table.TransformColumnTypes(删除的其他列,{{"页面编号", type text}, {"部门序号", type text}}),
    分组的行 = Table.Group(更改的类型, {"部门名称","部门序号"}, {{"页面权限", each Text.Combine([页面编号],";"), type nullable text}}),
    添加的后缀 = Table.TransformColumns(分组的行, {{"页面权限", each _ & ";", type text}})
in
    添加的后缀
部门名称权限
let
    源 = Excel.CurrentWorkbook(){[Name="角色配置表"]}[Content],
    删除的其他列 = Table.SelectColumns(源,{"角色编号", "角色名称", "快速配置"}),
    筛选的行 = Table.SelectRows(删除的其他列, each ([快速配置] <> null)),
    按分隔符拆分列 = Table.SplitColumn(Table.TransformColumnTypes(筛选的行, {{"快速配置", type text}}, "zh-CN"), "快速配置", Splitter.SplitTextByDelimiter("+", QuoteStyle.Csv), {"快速配置.1", "快速配置.2"}),
    用位置分列 = Table.SplitColumn(按分隔符拆分列, "快速配置.1", Splitter.SplitTextByRepeatedLengths(1), {"快速配置.1.1", "快速配置.1.2", "快速配置.1.3", "快速配置.1.4", "快速配置.1.5", "快速配置.1.6"}),
    按分隔符拆分列1 = Table.SplitColumn(用位置分列, "快速配置.2", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"快速配置.2.1", "快速配置.2.2"}),
    逆透视的其他列 = Table.UnpivotOtherColumns(按分隔符拆分列1, {"角色编号","角色名称"}, "属性", "序号"),
    删除的列 = Table.RemoveColumns(逆透视的其他列,{"属性"}),
    合并的查询 = Table.NestedJoin(删除的列, {"序号"}, 部门名称权限, {"部门序号"}, "部门名称权限分组", JoinKind.LeftOuter),
    #"展开的“部门名称权限分组”" = Table.ExpandTableColumn(合并的查询, "部门名称权限分组", {"页面权限"}, {"页面权限"}),
    已添加自定义 = Table.AddColumn(#"展开的“部门名称权限分组”", "页面权限合并", each if [页面权限] = null then [序号] else [页面权限]),
    分组的行 = Table.Group(已添加自定义, {"角色编号","角色名称"}, {{"页面权限", each Text.Combine([页面权限合并],";"), type nullable text}}),
    替换的值 = Table.ReplaceValue(分组的行,";;",";",Replacer.ReplaceText,{"页面权限"})
in
    替换的值
角色页面权限

最后使用Vlookup函数,将不同角色名称对应的页面权限取值到角色表中即可。

用户权限

用户权限表可以无限向下或向右拓展,增加的列字段会在进入Power BI时,自动做转换处理。

需要给当前本地电脑,配置一个账户,使用UserPrincipalName()返回本地电脑的账户,并赋予所有权限。

用户名称  新增使用用户姓名
用户账号  用户使用账号
角色名称  角色名称 该部分可以通过做Excel数据有效性取值角色权限表中角色名称列  
页面权限  使用函数,通过角色名称匹配到该角色拥有的页面权限
城市      使用函数,通过角色名称匹配到该角色拥有的城市权限
产品类别  使用函数,通过角色名称匹配到该角色拥有的产品类别权限
用户权限
加载页面配置表

筛选保留是否启用字段中不是null的值,调整数字字段为整数,正常加载。
添加部门编号辅助列,为模型中关系建立做准备。

let
    源 = Excel.Workbook(File.Contents("D:\桌面\学习课程\权限控制\权限控制配置表.xlsx"), null, true),
    页面配置表_Table = 源{[Item="页面配置表",Kind="Table"]}[Data],
    更改的类型 = Table.TransformColumnTypes(页面配置表_Table,{{"自然序号", Int64.Type}, {"内容序号", Int64.Type}, {"部门序号", Int64.Type}, {"页面序号", Int64.Type}, {"页面编号", Int64.Type}, {"是否启用", Int64.Type}}),
    筛选的行 = Table.SelectRows(更改的类型, each ([是否启用] = 1)),
    复制的列 = Table.DuplicateColumn(筛选的行, "页面编号", "部门编号辅助"),
    更改的类型1 = Table.TransformColumnTypes(复制的列,{{"部门编号辅助", type text}})
in
    更改的类型1
加载结果
加载权限配置表

只要你做的配置表字段跟我一样,那么就可以根据M公式还原出操作步骤。

let
    源 = Excel.Workbook(File.Contents("D:\桌面\学习课程\权限控制\权限控制配置表.xlsx"), null, true),
    权限配置表_Table = 源{[Item="权限配置表",Kind="Table"]}[Data],
    更改的类型 = Table.TransformColumnTypes(权限配置表_Table,{{"用户名称", type text}, {"用户账号", type text}, {"角色名称", type text}, {"页面权限", type text}, {"地区", type text}, {"产品类别", type text}}),
    逆透视的其他列 = Table.UnpivotOtherColumns(更改的类型, {"用户名称", "用户账号", "角色名称"}, "权限类型", "拥有权限"),
    生成权限列表 = Table.TransformColumns(#"逆透视的其他列", {{"拥有权限", each List.RemoveItems( Text.Split( _ ,";" ) , {""} ) }}),
    #"展开的“拥有权限”" = Table.ExpandListColumn(生成权限列表, "拥有权限"),
    更改的类型1 = Table.TransformColumnTypes(#"展开的“拥有权限”",{{"拥有权限", type text}}),
    筛选的行 = Table.SelectRows(更改的类型1, each [拥有权限] <> null)
in
    筛选的行
加载结果
处理表关系

不需要做任何表关系处理,也不需要做角色配置。

计算组准备

需要使用Tabular Editor软件来完成。

连接需要操作的模型



在Tables文件夹上右击选择创建计算组
创建计算组
新建一个组,并根据自己的喜好命名,这里命名为启用

点击启用,右侧填写以下内容:

CALCULATE( 
    SELECTEDMEASURE() ,
    KEEPFILTERS( TREATAS( CALCULATETABLE( VALUES( '权限配置表'[拥有权限] ) , '权限配置表'[用户账号] = USERPRINCIPALNAME( ) , '权限配置表'[权限类型] = "地区" ) , '地理位置'[地区] ) ) ,
    KEEPFILTERS( TREATAS( CALCULATETABLE( VALUES( '权限配置表'[拥有权限] ) , '权限配置表'[用户账号] = USERPRINCIPALNAME( ) , '权限配置表'[权限类型] = "产品类别" ) , '产品'[产品类别] ) ) ,
    KEEPFILTERS( TREATAS( CALCULATETABLE( VALUES( '权限配置表'[拥有权限] ) , '权限配置表'[用户账号] = USERPRINCIPALNAME( ) , '权限配置表'[权限类型] = "页面权限" ) , '页面配置表'[部门编号辅助] ) ) ,
    { "可以添加其他筛选" }
)
BPI中看到的效果
制作导航页
导航栏

看不懂的需要补一下Power BI按钮的知识。

用户 当前用户 = "当前用户:" &  SELECTEDVALUE( '权限配置表'[用户名称], "管理员" )
模型日期 = MAXX( all( '订单' ), '订单'[订单日期] ) 
首页 页面控制 导航目标 2000 = //页导航 目标
var t1 = SUMMARIZE( FILTER( '页面配置表', '页面配置表'[页面编号] = 2000 ),'页面配置表'[报表标题] )
var t2 = UserPrincipalName() in VALUES( '权限配置表'[用户账号] )
return if( t2, t1, BLANK() )

首页 页面控制 悬浮文字 2000 = //页导航 工具提示
var t1 = SUMMARIZE( FILTER( '页面配置表', '页面配置表'[页面编号] = 2000 ),'页面配置表'[页面标题] )
var t2 = UserPrincipalName() in VALUES( '权限配置表'[用户账号] )
return if( t2, t1, BLANK() )

首页 页面控制 按钮文本 2000 = //按钮文本
var t1 = SUMMARIZE( FILTER( '页面配置表', '页面配置表'[页面编号] = 2000 ),'页面配置表'[页面标题] )
var t2 = UserPrincipalName() in VALUES( '权限配置表'[用户账号] )
return if( t2, t1, BLANK() )

首页 页面控制 文本颜色 2000 = //边框颜色 或 字体颜色
var t1 = SUMMARIZE( FILTER( '页面配置表', '页面配置表'[页面编号] = 2000 ),'页面配置表'[页面标题] )
var t2 = UserPrincipalName() in VALUES( '权限配置表'[用户账号] )
var t3 = if( t1 <> BLANK() && t2, "#1a1a1a", "#cccccc" )
return t3

可以实现有权限的高亮显示,无权限的浅灰色显示的效果。

公告栏 = 
"<br>"&
"1、成功祝贺Test公司上市,股票代码 000000,欢迎关注!"&
"<br>"&
"<br>"&
"2、Test年销售额突破100亿,连续5年增长超30%,成为行业新星,获得独角兽企业提名;"&
"<br>"&
"<br>"&
"3、东北区连续3年出现下滑20%,建议重新评估市场前景!"

将度量值[导航栏 保留]放到值中,修改度量值展示名称为“_”,缩小字段宽度至最小,达到隐藏的效果。

特别注意:该步骤操作时,必须将本地电脑的用户账号放入Excel配置表中,否则将无权限查看任何数据。使用UserPrincipalName(),判断当前电脑本地用户账名,然后作为账号维护。

为了安全该处没有对没账号的用户赋予所有权限,而是必须维护本地账号才可以查看。

导航栏 保留 = //用于筛选出符合当前用户权限的页面,需要提前将本地电脑的用户账号添加到Excel配置表中,使用函数UserPrincipalName(),计算出当前电脑的账户。
COUNT( '页面配置表'[自然序号] )

这里讲下为何要增加这样一个操作,权限配置表中的权限是通过TREATAS函数与其他维度表挂在一起,其中页面控制部分也是一样,因为[部门名称]、[页面描述]都是在行上,本身无交互,只有当前用户页面权限和与页面相关的度量值交互时,才能产生筛选效果。

举例说明,比如使用地区做权限控制,如果只放地区到矩阵,则无法直接产生只有当前用户有权限的地区分类,若增加一个销售件数产生交互,则可以达到筛选的效果。

这个原因是因为计算组只能对度量值起作用,无法对表进行筛选。

导航栏 文本 = //显示文本
var t1 = SELECTEDVALUE( '页面配置表'[页面标题] )
return if( ISBLANK( t1 ), "🛑 请选择一个页面", "💡 点击进入" )

导航栏 显示 = //悬停提示
SELECTEDVALUE( '页面配置表'[页面描述] )

导航栏 目标 = //导航目标页名称
var t1 = SELECTEDVALUE( '页面配置表'[页面标题] )
var t2 = UserPrincipalName() in VALUES( '权限配置表'[用户账号] )
return if( ISBLANK( t1 ) || ISBLANK( t2 ), "", t1)

导航栏 颜色 = //符合选择条件和不符合选择条件的颜色调整,不唯一为红色,唯一为绿色。
var t1 = [导航栏 目标]
return if( ISBLANK( t1 ) || COUNTROWS( VALUES( '页面配置表'[页面描述] ) ) <> 1, "#FD625E", "#01B8AA" )
创建报表页

以上已经完成权限控制的部署,剩下的就是完成页面的调整。

单击报表页面空白处,将页面配置表[报表标题],放入可视化-保留所有筛选器,筛选该页需要显示的标题。

然后,添加AOP设置.权限,选择设置权限控制+页面控制,加入筛选后,选择启用

权限配置
导航栏 报表标题 = SELECTEDVALUE( '页面配置表'[报表标题] ) 

导航页 返回提示 = "点击返回导航页" 

导航页 返回目标 = "导航页" 

复制该页,并对页面重命名为页面配置表[页面标题]字段包含的报表页名称,同时筛选该页对应的[报表标题]。

后续有新增页面直接复制原来的报表,然后做相应的调整。

这样一个成熟简单的Power BI方案就部署完成了。

配置表的使用

权限配置表,是在Excel中配置,可以直接加载Excel文件到Power BI模型,如果是报表服务器部署方案,建议是放到服务器上修改,或者也可以将权限配置表导入到数据库。

后记

历时两周,终于写完了,期间搭建模型花费了一周的时间,各种调试,写文章实在是太浪费时间了,尤其是这种文章,整体框架又大,不太容易写清楚,换了好几个方案,最终才定的这个。

这个方案解决的是在Power BI当中的权限控制,至于员工入职或离职新增的账号如何自动同步让Power BI加载,这又是另一个话题了,如果有机会,我会继续写这部分的内容。

原来是打算写的非常详细照顾一下初学者,但是有些知识如果无限拓展,可能会造成文章主题重点不突出,因此部分Power BI基本知识就一概而过了,如果很多内容还看不懂,可能还需要补充一些其他的知识。

写之前特意找佐罗老师咨询了下,因为涉及到收费课程中的内容,得到佐罗老师允许后,才有了现在这篇文章,在此非常感谢佐罗老师。

个人水平有限,文章也有很多不足之处,忘不惜赐教。

参考资料来源:

上一篇 下一篇

猜你喜欢

热点阅读