保护嵌入式 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 登录,我们将需要:
该流程可能看起来像这样:
- 客户登录我们的 Web 应用。
- 我们的应用后端根据登录时使用的客户电子邮件查找客户的
account_id。 - 我们的应用后端使用 Metabase 的密钥通过签名 JWT 生成嵌入 URL。签名 JWT 编码查询参数,用于将“客户”仪表板筛选为
Account ID = account_id。 - Metabase 在静态嵌入 URL 返回筛选后的仪表板。
- 我们的应用前端在 iframe 中显示筛选后的仪表板。
有关代码示例,请参阅静态嵌入参考应用。
模块化和交互式嵌入身份验证(使用 JWT 或 SAML)
模块化嵌入(使用嵌入式分析 SDK 或 JS 组件)和交互式完整应用嵌入与 SSO(JWT 或 SAML)集成,在一个流程中进行身份验证和授权。身份验证集成使得将用户属性(如用户的角色或部门)映射到精细的数据访问级别变得容易,包括:

此图显示了交互式嵌入如何通过 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的用户属性,并为每个将使用 Metabase 的人将其设置为tenant或customer。 - 在 Metabase 中创建两个组:租户和客户。
- 在 Metabase 和我们的 IdP 之间同步组会员资格,以便:
- 具有
role=tenant的用户被分配到租户组。 - 具有
role=customer的用户被分配到客户组。
- 具有
- 为每个组设置“客户”表的行级别安全:
- 对于客户组,“客户”表将受
Account ID = primary_id限制。 - 对于租户组,“客户”表将受
Tenant 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 的文档。