为嵌入式 Looker 内容实现行级细分

作者:Christopher Seymour(高级数据分析师)和 Dean Hicks(开发者关系工程师)

借助行级细分,您可以根据一个或多个数据库字段中存储的值,限制单个用户可以访问的数据。本指南介绍了在嵌入式 Looker 内容中实现行级细分的方法,具体包含以下各部分:

简介

Looker 的嵌入功能是 Looker 产品最强大、最有价值的功能之一。如果您正在阅读本指南,很可能您已经将 Looker 内容嵌入到应用中,或者打算在不久的将来执行此操作。

本指南旨在帮助您更好地了解 Looker 嵌入功能的设计,以及如何在应用中实现细分,让合作伙伴可以管理对多个品牌的访问权限。在深入了解这个主题时,您需要阅读一些很长的内容,因此请注意,本指南并不是要快速修复一个简单问题,而是用作基础组件,帮助您更好地管理整个 Looker 嵌入用例。

用例概览

本指南介绍了一种常见用例:贵公司在产品中嵌入 Looker 内容,并为应当查看不同数据切片的用户细分提供服务。

对于这个带签名的嵌入用例,假设您是 Looker 实例的管理员。您可以与两种类型的外部嵌入用户合作:客户只能访问与其特定品牌相关的数据,合作伙伴则可以访问多个品牌的数据。您有一个包含多个功能块的信息中心,您会向使用您产品的每位客户显示该信息中心,但您需要为每位客户自动过滤该信息中心,以便信息中心仅显示特定于该客户的数据。本文档中的示例使用两家虚构的公司:HooliPied Piper

您有一个名为 products 的表格,其中显示了不同品牌的一些商品指标。每个品牌在已签名的嵌入应用中都对应于不同的嵌入用户(具有不同的 external_user_id)。由于每个嵌入式用户都应只能看到自己品牌的数据,因此您可以创建一个探索,在其中使用品牌用户属性的访问权限过滤条件:

explore: products {
  access_filter: {
    field: products.brand
    user_attribute: brand
  }
}

您有一个基于此“探索”的信息中心,它有两个图块:一个显示品牌名称,另一个显示该品牌的商品数量。

您可以使用 create_sso_embed_url 端点为每个嵌入用户生成此信息中心的嵌入网址。 此示例使用了两个品牌:Pied Piper 和 Hooli。以下是您在 Pied Piper 的 create_sso_embed_url 调用中使用的请求正文,其中 external_user_idpied_piper

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "pied_piper",
  "first_name": "PiedPiper",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper"}
}

您为 Pied Piper 生成的网址会以如下方式显示信息中心:

以下是 Hooli 的 create_sso_embed_url 调用中使用的请求正文,其中包含 external_user_id hooli

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "hooli",
  "first_name": "Hooli",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Hooli"}
}

为 Hooli 生成的网址会以如下方式显示信息中心:

Voilà!系统会根据每个嵌入用户的品牌用户属性值来过滤信息中心。

深入了解

太棒了!但是,如果我想向单个用户授予对多个品牌的访问权限,该怎么办?如何确保只有相关用户才能看到我的数据?

好消息!Looker 的签名嵌入功能旨在让开发者能够为用户打造强大的定制数据体验,同时确保数据模型和内容访问权限政策定义的数据治理能够得到维护。

确保数据治理的密封性对于提供出色的数据体验至关重要。请继续阅读,了解一些概念和最佳实践,您可以利用它们来设计最适合自己的体验。首先,我们简要介绍一下所有这些功能的运作方式。

Looker 签名嵌入的基础知识

请务必注意,Looker 在嵌入式环境中的用户身份验证和管理方式与非嵌入式环境中的用户身份验证和管理方式以及大多数其他 Web 应用中的用户身份验证和管理方式基本相同。

在 Looker 签名嵌入上下文中,这可能会令人困惑,因为签名身份验证步骤、用户设置和信息中心本身都合并到一个长而复杂的网址中。不过,该网址用于建立会话,即使网址经过缩短,该规则仍然适用。牢记这一点有助于您成功打造出色的数据体验。

已签名的嵌入网址结构

以下是 create_sso_embed_url 调用使用 Pied Piper 的请求正文生成的签名嵌入身份验证网址之一:

https://mylookerinstance.cloud.looker.com/login/embed/%2Fembed%2Fdashboards%2F17?permissions=%5B%22access_data%22%2C%22see_user_dashboards%22%5D&models=%5B%22thelook%22%5D&signature=iG6vcKBgnA50jaL2iShFeQHwFPN7wvTx7Rz6r%2FtFuvE%3D&nonce=%22967729518a7dbb8a178f1c03a3511dd1%22&time=1696013242&session_length=300&external_user_id=%22pied_piper%22&access_filters=%7B%7D&first_name=%22Pied%22&last_name=%22Piper%22&user_attributes=%7B%22brand%22%3A%22Pied+Piper%22%7D&force_logout_login=true

以下是已解码并拆分为单独一行的同一网址:

https://mylookerinstance.cloud.looker.com/login/embed/
/embed/dashboards/17
?permissions=["access_data","see_user_dashboards"]
&models=["thelook"]
&signature=iG6vcKBgnA50jaL2iShFeQHwFPN7wvTx7Rz6r/tFuvE=
&nonce="967729518a7dbb8a178f1c03a3511dd1"
&time=1696013242
&session_length=300
&external_user_id="pied_piper"
&access_filters={}
&first_name="PiedPiper"
&last_name="User"
&user_attributes={"brand":"Pied Piper"}
&force_logout_login=true

访问此网址后,会发生以下情况:

  1. Looker 会查找 external_user_id = pied_piper 的现有用户账号。如果不存在,Looker 会使用该 external_user_id 创建一个新的用户账号。

  2. 现有用户的账号详细信息,包括权限、模型、群组(如果已指定)、用户属性值(如果已指定)将被该网址中指定的账号详细信息覆盖。

  3. Looker 会在浏览器中存储会话 Cookie,以便对用户进行身份验证并为该用户建立会话。

  4. 然后,Looker 会重定向到 create_sso_embed_url 调用中指定的目标网址或重定向网址:

    https://mylookerinstance.cloud.looker.com/embed/dashboards/17

    在原始的签名嵌入网址中,您可以看到此重定向网址显示为经过编码的相对网址:

    %2Fembed%2Fdashboards%2F17

虽然第 1-3 步会自动在后台进行,最终用户只会看到最终结果(信息中心本身),但这些步骤与非嵌入式 Looker 的常规用户进行身份验证的步骤基本相同。假设您希望用户使用用户和密码凭据登录。该流程如下所示:

  1. 您(Looker 管理员)前往“管理 - 用户”面板,使用搜索栏检查此用户是否已存在用户账号。如果没有,请创建一个新用户账号。

  2. 您(Looker 管理员)在“管理”-“用户”面板中点击用户旁边的修改,为用户配置权限、模型、群组、用户属性值和其他值。

  3. 用户前往 https://mylookerinstance.cloud.looker.com/login 登录页面,然后输入其用户名和密码。Looker 会在浏览器中存储会话 Cookie,以便对用户进行身份验证并为该用户建立会话。

  4. 然后,Looker 会重定向到着陆页(通常为 https://mylookerinstance.cloud.looker.com/browse)。

请注意,会话 Cookie 将应用于浏览器窗口中的每个标签页。如果用户在 https://mylookerinstance.cloud.looker.com/browse 上启动,打开一个新的浏览器标签页,然后前往其权限授予的任何页面,那么页面将使用原始浏览器标签页中已创建的会话 Cookie 按预期加载。

嵌入用户也是如此。嵌入用户在界面中可以访问的页面受到一些限制,他们只能访问带有 /embed 前缀的“探索”“信息中心”和“探索”网址。不过,他们仍然可以自由手动前往用户账号详细信息授予其访问权限的任何信息中心。假设原始已签名嵌入网址会将您重定向到一个浏览器标签页中的 https://mylookerinstance.cloud.looker.com/embed/dashboards/17。然后,您打开一个新的浏览器标签页,并加载位于同一个文件夹中的不同嵌入信息中心(因此访问限制相同): https://mylookerinstance.cloud.looker.com/embed/dashboards/19

虽然原始已签名嵌入网址中指定的重定向网址是指信息中心 17,但您可以看到,如果您在浏览器标签页中手动输入该网址,信息中心 19 会按预期加载。请注意,加载其他信息中心不需要另一个签名的嵌入网址。

这里的关键洞见是,在网址中建立的所有用户账号详细信息(权限、用户属性等)都会应用于整个用户会话,而不仅仅是应用于原始已签名网址中指定的特定信息中心。换句话说,顾名思义,用户属性是用户的属性,而不是信息中心的属性,应用于确定特定用户在整个应用(而不仅仅是某个特定标签页)中的访问权限级别。

同时访问多个品牌

请注意,您还有可能拥有或管理多个品牌的外部合作伙伴。在此示例中,合作伙伴管理 Pied Piper 和 Hooli 品牌。

非嵌入方式

已签名的嵌入式用户会话在基本原理上与常规的非嵌入式 Looker 用户会话相同,因此,将之前在常规的非嵌入式 Looker 用户会话上下文中介绍的存在问题的方法重新构建并细分这些步骤,有助于了解如何以更稳健的方式实现此解决方案。如果您要向有权访问 Looker 界面的标准 BI 用户提供说明,工作流程将如下所示:

  1. 您需要在“管理 - 用户”页面上创建两个不同的用户账号。
    1. 在第一个用户账号的修改页面上,您将 brand 用户属性值设置为 pied_piper
    2. 在第二个用户账号的修改页面上,您将 brand 用户属性值设置为 hooli
  2. 您将这两个用户账号的账号设置电子邮件发送给合作伙伴。
  3. 合作伙伴为每个账号设置单独的电子邮件地址和密码凭据。
  4. 您向合作伙伴提供信息中心链接。(https://mylookerinstance.cloud.looker.com/dashboards/17),并告知他们,如需在品牌之间切换信息中心,他们需要返回另一个标签页中的登录页面,输入其他用户账号的电子邮件地址和密码凭据,然后在该标签页中重新加载信息中心。

合作伙伴会按照说明操作。但是,在第二个浏览器标签页中输入 Hooli 用户账号的用户名和密码,然后返回到已加载 Pied Piper 信息中心的第一个标签页后,合作伙伴点击了重新加载按钮。令合作伙伴惊喜的是,信息中心显示了 Hooli 数据!

到目前为止,您可能在想:

等等...这太不方便了。那么,最好的方法是什么?

当然可以!这些场景有助于说明一个在非嵌入式情境中已经很简单的原则,但可能会被嵌入式情境的抽象所掩盖:单个真实用户应与单个 Looker 用户账号相关联,且该账号应具有一组用户属性值。签名嵌入文档中对 external_user_id 的解释中也清楚地说明了这一点。

Looker 使用 external_user_id 区分已签名的嵌入用户,因此必须为每个用户分配一个唯一 ID。

您可以使用您喜欢的任何字符串为用户创建 external_user_id,只要该字符串对于该用户而言是唯一的。每个 ID 都与一组权限、用户属性和模型相关联。单个浏览器一次只能支持一个 external_user_id(即用户会话)。会话期间无法对用户的权限或用户属性进行任何更改。

同时访问多个品牌 - 错误做法

与任何可自定义的解决方案一样,您应该避免使用某些方法。例如,在下面这个实现中,您的应用会使用之前显示的 create_sso_embed_url 调用中的相同输入为这两个 external_user_ids 生成网址,并在应用中创建一个新标签页,用于加载合作伙伴需要访问的每个信息中心。我们经常看到开发者实现这样的解决方案,这会导致用户的工作流不正确:

  1. 前往“Pied Piper”信息中心标签页。
  2. 前往“Hooli 信息中心”标签页。
  3. 返回 Pied Piper 信息中心标签页。
  4. 点击 Pied Piper 信息中心内的重新加载按钮。

... Pied Piper 信息中心会显示 Hooli 数据!

您可以尝试使用类似的方法,但要为这两个 create_sso_embed_url 调用使用相同的 external_user_id test_user。但其行为完全相同 - 使用 Pied Piper 信息中心重新加载该标签页后,它会显示 Hooli 的数据。

如何确保每个品牌的信息中心仅显示该品牌的数据?

运用最佳实践

如需应用“从非嵌入式角度来看的方法”部分中介绍的方法,您需要单个用户属性值,该属性值可授予对合作伙伴应有权访问的整个应用中的所有数据的访问权限。为此,您可以对 brand 属性 Pied Piper,Hooli 使用逗号分隔值:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper,Hooli"}
}

为了让此语法正常运行,您需要确保将用户属性设置为字符串过滤器(高级)

请注意,如果用户的数据访问权限级别发生变化,您仍然可以更改用户的一组用户属性。例如,如果合作伙伴拥有第三个品牌的所有权,那么您就可以将第三个品牌添加到您为其 brand 用户属性指定的逗号分隔列表中。这样,当他们退出并重新登录时,更改就会生效。

过滤信息中心结果

好的,我了解到,用户属性需要指定用户可以在应用中访问的所有数据。但是,如果我以这种方式指定用户属性,信息中心就会显示所有这些品牌的数据!如何将特定信息中心的结果范围缩小到特定品牌?

过滤特定信息中心的正确方法是使用常规的信息中心过滤条件!(这现在可能看起来很明显,但我们发现有些人会陷入用户属性是应用嵌入内容上下文中过滤条件的唯一方式的误区,这可能是因为 user_attributes 是已签名嵌入网址中的参数,而过滤条件不是。)

请务必要求提供过滤条件值,并使用“单选”控件选项之一(例如下拉菜单):

确保过滤条件应用于所有必要图块上的正确字段:

现在,您的合作伙伴只能在这两个值(且仅在这两个值)之间进行选择,因为下拉菜单中的可用选项受用户属性的限制:

预填充信息中心过滤条件

好的,现在可按特定品牌过滤信息中心,但我希望在用户在应用中加载该品牌的信息中心时,将该过滤条件值设为特定品牌。

同样,思考一下在非嵌入式环境中是如何运作的也很有帮助。如何向已应用特定过滤条件值的信息中心发送链接?您可能已经注意到,当您选择过滤器值时,该过滤器值会显示在信息中心的网址中:

将网址的那部分添加到 create_sso_embed_url 调用的 target_url 中:

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17?Brand=Hooli",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper,Hooli"}
}

如果您使用的是嵌入 SDK,则可以使用 withFilters 指定要应用于嵌入内容的初始过滤条件:

https://looker-open-source.github.io/embed-sdk/classes/EmbedBuilder.html#withFilters

如果您使用的是自己的自定义脚本,请确保在路径编码之前将过滤条件添加到网址。某些值可能已在过滤条件字符串中编码(例如,?Brand=Pied+Piper 中有一个空格编码为 +),因此这些值将在最终到达网址中进行双重编码。您可以参阅“SO 嵌入式信息中心 - 将信息中心过滤条件设置为网址的一部分?”请参阅 Looker 社区帖子,了解这些细微差别。如果您在应用滤镜时仍遇到问题,也可以在该社区帖子中添加任何问题。

隐藏信息中心过滤条件

好的,我已经知道如何在信息中心设置初始过滤条件,但我也不希望合作伙伴自行更改信息中心过滤条件,过滤条件值应该只能根据合作伙伴在我的应用中导航到的信息中心来确定。如何隐藏信息中心过滤条件?

为此,您可以使用主题。主题是一项付费功能,因此,如果您尚未在 Looker 实例上启用此功能,请与您的 Looker 销售团队联系,以启用此功能。

启用该功能后,前往“管理”-“主题”页面的信息中心控件部分,然后取消选中显示过滤条件栏选项:

然后,您可以将主题设为默认主题,也可以在特定信息中心的网址中应用主题。同样,这会进入 create_sso_embed_url 调用的 target_url

{
  "target_url": "https://mylookerinstance.cloud.looker.com/embed/dashboards/17?Brand=Hooli&theme=test_theme",
  "session_length": 300,
  "force_logout_login": true,
  "external_user_id": "test_user",
  "first_name": "Test",
  "last_name": "User",
  "permissions": ["access_data","see_user_dashboards"],
  "models": ["thelook"],
  "user_attributes": {"brand":"Pied Piper,Hooli"}
}

如需详细了解如何隐藏嵌入信息中心过滤条件(包括一些嵌入的 SDK 代码段),请参阅 YouTube 教程使用自定义过滤条件嵌入 Looker

最终结果应与原始问题中的用户体验完全相同:

但现在,由于过滤条件值是在该应用中嵌入的相应目标网址中编码的,因此即使您在标签页之间来回切换,各个品牌的信息中心也始终会显示已过滤出正确的品牌的信息中心。

以其他用户的身份进行测试

现在,用户体验确实非常接近我最初设想的。但在我的用例中,合作伙伴具有与 external_user_id=pied_piperexternal_user_id=hooli 的各位用户不同的权限和其他用户设置,这会导致界面中提供的选项不同,只是整体的用户体验略有不同。我想允许用户看到 pied_piper hooli 用户看到的所有内容,就好像这个人实际上以这些用户身份登录一样。如何才能做到这一点?

如果您希望用户能够以每个品牌用户的身份进行测试,那么您可以在应用中构建类似的 sudo 函数,该函数会在用户以 Pied Piper 用户的身份加载 external_user_id=pied_piper 的嵌入网址,以及用户以 Hooli 用户身份时加载 external_user_id=hooli 的嵌入网址。如果您的应用使用该 API,您还可以使用 login_user API 端点以品牌用户身份使用该 API 执行 sudo 操作。

但是,如果您再想一想非嵌入上下文:在“管理 - 用户”页面中,您不能同时通过 sudo 作为多个非嵌入用户在多个标签页中使用 sudo 会话,而 sudo 会话会应用于整个浏览器窗口,就像其他所有用户会话一样。因此,您应将 sudo 设计为一次仅一个用户执行 sudo。仔细想想,这种设计更符合完美模拟您以 sudo 身份运行的用户的体验。例如,pied_piper 用户只能访问 Pied Piper 信息中心,而无权在其他标签页中打开其他信息中心。因此,当您以此用户身份使用 sudo 时,也无法在不同的标签页中打开不同的信息中心。

总结

希望本指南对您有所帮助,并让您做好了继续构建 Looker 签名嵌入内容的准备! 我们一直致力于将 Looker 打造成最灵活、最强大的嵌入式数据分析产品,期待收到您的反馈!如果您有任何疑问或想了解更多信息,可以在 Looker 社区中与我们互动,并参加我们的社区活动。