保护嵌入式 Metabase
通过认证和授权保护嵌入内容
在互联网上保护内容有两种基本方式:
在本指南中,我们将主要讨论认证。
公共嵌入
公共嵌入不涉及任何认证或授权。公共嵌入显示一个公共链接,末尾带有一个唯一字符串,如下所示:
https://my-metabase.com/public/dashboard/184f819c-2c80-4b2d-80f8-26bffaae5d8b
该字符串(在本例中为:184f819c-2c80-4b2d-80f8-26bffaae5d8b
)唯一标识您的 Metabase 问题或仪表板。由于公共嵌入不进行任何认证或授权,任何拥有该 URL 的人都可以查看数据。
示例:公共链接中的过滤器不保护数据
那么,如何利用公共嵌入呢?假设我们有一个仪表板显示账户数据:
账户 ID | 计划 | 状态 |
---|---|---|
1 | 基本 | 活跃 |
2 | 基本 | 活跃 |
3 | 基本 | 不活跃 |
4 | 高级 | 不活跃 |
5 | 高级 | 活跃 |
我们想添加一个“状态 = 活跃”过滤器,并在嵌入中显示仪表板的公共链接
账户 ID | 计划 | 状态 |
---|---|---|
1 | 基本 | 活跃 |
2 | 基本 | 活跃 |
5 | 高级 | 活跃 |
为了应用并隐藏“状态 = 活跃”过滤器,我们将在嵌入的公共链接末尾添加查询参数
https://my-metabase.com/public/dashboard/184f819c-2c80-4b2d-80f8-26bffaae5d8b?status=active#hide_parameters=status
即使我们已经从嵌入中隐藏了过滤器,但有人仍然可以获取嵌入中使用的公共链接,并删除查询参数?status=active
https://my-metabase.com/public/dashboard/184f819c-2c80-4b2d-80f8-26bffaae5d8b
在没有查询参数的情况下加载公共链接将从数据中移除“状态 = 活跃”过滤器。该用户将可以访问原始账户数据,包括包含不活跃账户的行。
静态嵌入通过 JWT 授权
静态嵌入使用 JWT 授权流程来完成两件事:
- 对资源(例如图表或仪表板的 URL)进行签名,以确保只有您的嵌入应用程序才能从您的 Metabase 请求数据。
- 对参数(例如仪表板过滤器)进行签名,以防止人们更改过滤器并获取其他数据。
静态嵌入没有用户会话
静态嵌入不会在 Metabase 端验证用户身份,因此用户无需创建 Metabase 账户即可查看静态嵌入。但是,如果没有 Metabase 账户,Metabase 将无法记住用户或其会话,这意味着:
- Metabase 权限以及行和列安全将不起作用——如果您需要锁定敏感数据,则必须为每个静态嵌入设置锁定参数。
- 静态嵌入中的任何筛选器选择将在签名 JWT 过期后重置。
- 所有静态嵌入使用情况将在使用情况分析中显示为“外部用户”。
静态嵌入安全性与交互式嵌入安全性
静态嵌入只保证对 Metabase 数据的授权访问(您决定哪些是可访问的)。
如果您想根据用户的身份(您决定谁可以访问什么)来保护您的静态嵌入,您需要设置自己的身份验证流程,并手动将其连接到每个静态嵌入上的锁定参数。请注意,锁定参数本质上是过滤器,因此您只能在静态嵌入中设置行级限制。
如果您想以更简单的方式为不同的客户嵌入不同的数据视图(而不允许客户查看彼此的数据),请了解交互式嵌入如何在一个流程中验证和授权用户。
使用 JWT 授权的静态嵌入
此图演示了嵌入如何通过签名的 JWT 进行保护
- 访问者到达:您的前端收到显示 Metabase 嵌入 URL 的请求。
- 已签名请求:您的后端生成一个包含已签名 JWT 的 Metabase 嵌入 URL。已签名 JWT 应编码您用于过滤数据的任何查询参数。
- 响应:您的 Metabase 后端根据签名 JWT 中编码的查询参数返回数据。
- 成功:您的前端显示嵌入的 Metabase 页面,并显示正确的数据。
示例:使用静态嵌入上的锁定参数保护数据
在公共嵌入示例中,我们向您展示了(可能不明智地)某人如何通过编辑其查询参数来利用唯一的公共链接。
让我们回到我们的账户示例
账户 ID | 计划 | 状态 |
---|---|---|
1 | 基本 | 活跃 |
2 | 基本 | 活跃 |
3 | 基本 | 不活跃 |
4 | 高级 | 不活跃 |
5 | 高级 | 活跃 |
请记住,我们可以通过在嵌入 URL 的末尾包含查询参数来过滤公共嵌入中的数据
https://my-metabase.com/public/dashboard/184f819c-2c80-4b2d-80f8-26bffaae5d8b?status=active
账户 ID | 计划 | 状态 |
---|---|---|
1 | 基本 | 活跃 |
2 | 基本 | 活跃 |
5 | 高级 | 活跃 |
使用静态嵌入时,我们可以通过在签名的 JWT 中编码查询参数来“锁定”过滤器。例如,假设我们将“状态 = 活跃”过滤器设置为锁定参数。 ?status=active
查询参数将被编码在签名的 JWT 中,因此在静态嵌入 URL 中将不可见或不可编辑
https://my-metabase.com/dashboard/your_signed_jwt
如果有人尝试在静态嵌入 URL 的末尾添加一个(未签名)查询参数,如下所示
https://my-metabase.com/dashboard/your_signed_jwt?status=inactive
Metabase 将拒绝此未经授权的数据请求,因此不活跃的账户行将继续对嵌入隐藏。
示例:将用户属性发送到锁定参数
假设我们希望将“账户”表暴露给客户,以便客户可以根据“账户 ID”查找某一行。
账户 ID | 计划 | 状态 |
---|---|---|
1 | 基本 | 活跃 |
2 | 基本 | 活跃 |
3 | 基本 | 不活跃 |
4 | 高级 | 不活跃 |
5 | 高级 | 活跃 |
如果我们想避免为每个客户创建 Metabase 登录,我们需要:
流程可能如下所示:
- 客户登录我们的网络应用。
- 我们的应用后端根据登录时使用的账户电子邮件查找客户的
account_id
。 - 我们的应用后端使用 Metabase 的秘钥来生成嵌入 URL,并附带一个签名的 JWT。签名的 JWT 编码了查询参数,以根据
Account ID = account_id
过滤账户仪表板。 - Metabase 在静态嵌入 URL 处返回过滤后的仪表板。
- 我们的应用前端在 iframe 中显示过滤后的仪表板。
有关代码示例,请参阅静态嵌入参考应用。
交互式嵌入在一个流程中进行身份验证和授权
交互式嵌入与 SSO(JWT 或 SAML)集成,在一个流程中完成用户身份验证和授权。身份验证集成使得将用户属性(例如用户的角色或部门)映射到精细的数据访问级别变得容易,包括:
通过 SSO 进行交互式嵌入
此图向您展示了交互式嵌入如何通过SSO进行保护
- 访客到达:您的前端收到显示所有内容(包括 Metabase 组件,如 React 组件)的请求。
- 加载嵌入:您的前端组件使用嵌入 URL 加载 Metabase 前端。
- 检查会话:为了在嵌入 URL 处显示数据,您的 Metabase 后端会检查有效的会话(已登录的访客)。
- 如果没有有效的会话:
- 重定向到 SSO:您的 Metabase 前端将访客重定向到您的 SSO 登录页面。
- SSO 认证:您的 SSO 流程验证访客身份,并根据其身份生成会话。会话信息应编码用户属性,例如组成员身份和行和列安全权限。
- 重定向到 Metabase:您的 SSO 流程将访客重定向到您的 Metabase 前端,并附带会话信息。
- 请求:您的 Metabase 前端将数据请求连同会话信息发送到 Metabase 后端。
- 响应:您的 Metabase 后端根据会话信息中编码的用户属性返回数据。
- 成功:您的前端组件显示嵌入的 Metabase 页面,并为已登录的访客显示正确的数据。
步骤 4 的机制会根据您使用 JWT 还是 SAML 进行 SSO 而有所不同。
示例:使用 SSO 和行/列安全保护数据
在我们的静态嵌入示例中,我们使用锁定参数来显示“账户”表的安全过滤视图。
交互式嵌入和 SSO 集成的优点在于,我们无需手动管理每个嵌入的锁定参数。相反,我们可以将身份提供商 (IdP) 中的用户属性映射到 Metabase 中的权限以及行和列安全。用户可以从第一次登录开始,就通过身份验证和授权来自助服务特定数据子集。
让我们扩展我们的“账户”示例,以包含“租户 ID”。“租户 ID”表示一组客户的父组织
租户 ID | 账户 ID | 计划 | 状态 |
---|---|---|---|
999 | 1 | 基本 | 活跃 |
999 | 2 | 基本 | 活跃 |
999 | 3 | 基本 | 不活跃 |
777 | 4 | 高级 | 不活跃 |
777 | 5 | 高级 | 活跃 |
我们仍然希望将“账户”表暴露给我们的客户,但有一些额外的要求
- 单个客户只能查看他们自己的账户 ID 的数据。
- 租户可以查看其所有子账户(但不能查看其他租户的数据)。
要设置这些多租户权限,我们需要:
- 在我们的 IdP 中创建一个
primary_id
属性,以唯一标识所有租户和客户。 - 在我们的 IdP 中创建一个名为
role
的用户属性,并将其设置为tenant
或customer
,适用于将使用 Metabase 的每个人。 - 在 Metabase 中创建两个组:租户和客户。
- 同步 Metabase 和 IdP 之间的组成员身份,以便
role=tenant
的用户被分配到租户组。role=customer
的用户被分配到客户组。
- 为每个组在“账户”表上设置行级安全
- 对于“客户”组,“账户”表将受
账户 ID = primary_id
的限制。 - 对于“租户”组,“账户”表将受
租户 ID = primary_id
的限制。
- 对于“客户”组,“账户”表将受
当租户 A 首次使用 SSO 登录时
- Metabase 将为其创建账户。
- 我们的 IdP 会将
role=tenant
和primary_id=999
属性发送到 Metabase。 - Metabase 将自动将租户 A 分配到租户组。
- 租户 A 将获得租户组的权限(包括行和列安全)。
- 租户 A 将在 Metabase 的任何位置看到“账户”表的受限视图
租户 ID | 账户 ID | 计划 | 状态 |
---|---|---|---|
999 | 1 | 基本 | 活跃 |
999 | 2 | 基本 | 活跃 |
999 | 3 | 基本 | 不活跃 |
当客户 1 登录时,他们将根据其role
和primary_id
属性看到“账户”表的不同筛选版本
租户 ID | 账户 ID | 计划 | 状态 |
---|---|---|---|
A | 1 | 基本 | 活跃 |
示例应用程序
延伸阅读
阅读其他版本的 Metabase 的文档。