你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在 Azure 中实现跨目录通信

本指南提供了一种解决方案,用于在不同 Microsoft Entra 目录管理的 Azure 订阅中托管的服务之间实现双向安全通信。

由于许多服务固有的限制,在 Azure 中保护跨目录通信可能会很困难。 可以使用 Azure 托管标识从 Microsoft Entra ID 获取令牌,从而无需直接管理凭据。 但是,Azure 托管标识不能跨目录边界工作,典型的替代方法是使用共享机密,例如共享访问签名 URL。 请记住,如果使用共享机密,则需要安全地分发和轮换Microsoft Entra 目录边界中的机密。

避免此类开销的一种做法是,创建一个多租户 Microsoft Entra 应用程序来代表你的工作负载标识。 通过许可过程,可以将此工作负荷标识已知到外部目录,并最终允许应用程序对外部目录中的服务进行身份验证。

本文给出了使用示例代码的该模式的示例实现。

对于需要跨 Microsoft Entra 目录边界进行通信的各种服务的任何方案,都可以重复使用此模式。

体系结构

跨目录通信体系结构的示意图。

下载此体系结构的 PowerPoint 文件

工作流

以下工作流与上图相对应:

  1. 提供程序端的管理员创建多租户 Entra 应用程序注册并为其设置客户端密码。

  2. 客户一端的管理员在其 Microsoft Entra 目录中预配服务主体。 此服务主体源于供应商创建的应用注册。 可以通过多种方法执行此步骤。 在此示例中,我们选择创建 URL 以提供给客户目录管理员,但你可以改用 Microsoft 图形 API。

  3. 客户将此基于角色的访问控制 (RBAC) 角色应用于此新服务主体,以便获得访问 Azure 服务总线的授权。

  4. 提供程序的函数应用程序使用应用程序注册的客户端 ID 和客户端密钥,将经过身份验证的消息发送到客户的服务总线队列。

  5. 客户的函数应用使用托管标识通过服务总线触发器从队列中读取提供商的消息。

  6. 收到消息后,客户的函数应用通常会在将状态消息发送回提供程序之前执行一些工作。 在这种情况下,出于演示目的,函数应用会立即向同一服务总线中单独队列上的提供程序发送一条状态消息。

  7. 此函数应用通过 Azure Functions 触发的计时器从客户目录中的状态队列读取信息。

方案详细信息

一个服务提供商有多个客户。 供应商和每位客户都有各自的 Microsoft Entra ID 和 Azure 资源。 提供程序和每个客户都需要一种安全的双向通信方法,以便他们可以通过服务总线队列交换消息。 解决方案应有引人注目的标识故事,避免引入不必要的凭据或机密。

在多租户 Entra 应用程序方面需要了解的知识

  • 应用程序对象是应用程序的全局唯一实例。

  • Microsoft Entra 中注册应用程序时,会自动在目录中创建应用程序对象和服务主体对象。

  • 在使用该应用程序并引用该应用程序对象的每个目录中创建服务主体对象。 应用程序对象与其对应的服务主体对象之间存在一对多关系。

  • 应用程序对象是应用程序的全局表示形式,用于所有目录。 服务主体对象是在特定目录中使用的本地表示形式。

  • 必须在使用应用程序的每个目录中创建服务主体对象,以便它可以建立一个标识来访问目录保护的资源。 单目录应用程序在其主目录中只有一个服务主体对象。 此服务主体对象被创建,并允许在应用程序注册期间使用。 多租户 Entra 应用程序在每个目录中创建服务主体对象,并且该目录中的一个用户已同意使用该对象。

  • 若要访问受Microsoft Entra 目录保护的资源,安全主体必须表示需要访问权限的实体。

  • 在注册或同意时,向应用程序授予访问目录中资源的权限时,将创建服务主体对象。 此体系结构通过同意流实现。

提供程序如何向客户发送消息?

理想情况下,提供程序能够将托管标识分配给负责将消息发送到客户的队列的 Azure 计算资源。 客户的目录配置为信任来自提供商目录的托管标识。 但是,两个 Microsoft Entra 租户之间的真正联合(本质上允许在两个目录之间“共享”标识)目前还无法实现。 因此,提供者需要使用客户认可的身份进行认证。 提供商需要以客户知晓的服务主体身份向客户的 Microsoft Entra 租户进行身份验证。

我们建议提供商在其自己的目录中注册一个多租户 Entra 应用程序,然后让每个客户在其目录中预配关联的服务主体。 然后,提供程序可以使用此服务主体向客户的目录和客户托管 API 进行身份验证。 在这种方法中,提供程序永远不需要共享客户端机密。 凭据管理由提供商独自责任。

客户如何向提供程序发送信息?

我们建议客户创建或托管一个队列,提供程序可以从中读取。 客户将消息写入队列。 提供商使用服务主体对象反复轮询每个客户队列以获取消息。 这种方法的缺点是,当提供方收到消息时,会产生轮询延迟。 代码还需要在提供程序中更频繁地运行,因为它必须唤醒并执行轮询逻辑,而不是等待事件触发它。 但是,凭据管理由提供程序全权负责,这增强了安全性。

另一种可能的解决方案是让提供程序为每个客户创建或托管队列。 每个客户都会创建自己的多租户 Entra 应用程序,并请求提供程序将其作为服务主体对象在其目录中预配。 客户随后使用此服务主体对象将消息发送到供应商端特定客户的队列。 凭据管理仍然完全由客户负责。 此方法的一个缺点是,提供程序必须将与客户应用程序关联的服务主体预配到其目录中。 此为手动过程,提供程序可能不希望手动步骤成为加入新客户的流程的一部分。

示例代码设置

以下步骤指导你完成在提供商和客户之间设置跨目录通信的过程。

提供程序安装程序

提供商设置包括用于生成和预配多租户 Entra 应用程序服务主体的步骤,以及用于预配客户目录的步骤。

  1. 创建一个 HTTP 触发的函数应用,用于将一条消息写入客户目录中的客户服务总线命令队列。

  2. 创建时间触发的函数应用,以定期检查客户目录中客户服务总线中的状态队列。

在提供程序的目录中创建多租户 Entra 应用程序

首先,在提供程序的目录中创建多租户 Entra 应用程序,并在客户的目录中预配该标识。 在这种情况下,标识是服务主体。 本文前面的体系结构部分介绍了如何设置和预配从提供商目录到客户目录的服务主体。 该体系结构还概述了如何使用多个 Microsoft Entra 租户进行预配。

  1. 选择多租户组织选项。

  2. 将以下网站添加为重定向 URI:https://entra.microsoft.com。 可以更改此 URI 以满足业务需求。

  3. 注册并记下应用程序(客户端)ID 值。

创建新客户端机密

  1. 创建多租户 Entra 应用程序后,为此服务主体创建客户端密码。

  2. 将生成的机密保存到安全位置。 机密和客户端 ID 是你的客户端凭据,需要使用这些凭据在授权代码流中交换代码,并在下一步骤中获取 ID 令牌。

Azure Functions - HTTP 触发

使用 HTTP 函数将消息发送到客户的服务总线部署队列,从而从提供程序的目录中启动部署。 我们选择了 HTTP 触发的函数作为交付方式,来开始这个概念验证。 之前生成的服务主体充当用于访问客户目录和写入服务总线内特定队列的凭据。 还需要完成客户设置,才能使此步骤正常工作。

Azure Functions - 计时器触发

使用计时器触发的函数从客户目录中轮询部署状态队列。 在这个概念验证中,我们每 10 秒轮询一次部署状态队列以进行演示。 此方法消除了客户需要拥有服务主体才能访问提供者目录的需求。

客户设置

  1. 通过修改和使用提供的 URL 来预配服务主体。

  2. 限定提供商服务主体的范围,以使用适当的 RBAC 控制。

  3. 创建服务总线触发的函数,以从服务总线消息队列中读取消息,并将消息放入另一个队列。 为了便于演示,此流最适合用于测试功能。

  4. 为服务总线触发的函数创建系统分配的托管标识。

  5. 分配系统分配的托管标识服务总线范围。

将服务主体从提供商目录预配到客户目录

  1. client_id 查询字符串参数替换为自己的客户端 ID 后,请访问以下 URL:https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize?response_type=code&response_mode=query&scope=openid&client_id=<your_client_ID>

    还可以使用管理员 Microsoft Graph API 调用、Azure PowerShell 命令或 Azure CLI 命令将服务主体预配到另一个 Microsoft Entra 租户。

  2. 使用客户目录中的帐户登录。

  3. 在同意屏幕上,选择 “接受 ”以在客户目录中预配提供程序的应用程序。 URL 最终会重定向到 microsoft.com,这仍可以达到将标识预配到客户目录的预期效果。

  4. 转到“企业应用程序”查看新预配的服务主体,以验证客户 Microsoft Entra 租户中的标识

为预配的服务主体设置 RBAC

在提供商服务主体设置中限定提供商服务主体的范围,以获得服务总线上的“服务总线数据所有者”角色。 此服务主体既用于向具有 HTTP 触发函数的队列写入,也用于从计时器触发函数中读取队列。 请确保将“Azure 服务总线数据所有者”角色添加到服务主体。

Azure Functions - 服务总线触发器

按照基于标识的函数教程中的步骤,从服务总线队列定义函数触发器,并了解如何设置托管标识。 本指南可帮助您在将消息添加到服务总线队列后,触发函数应用。 将消息放入不同的队列时,也要使用托管标识。 出于演示目的,我们使用同一函数推送消息。

在新创建的服务总线命名空间中,选择访问控制 (IAM)。 可以在控制平面中查看和配置谁有权访问资源。

使用托管标识向函数应用授予对服务总线命名空间的访问权限。

  1. 请确保将“Azure 服务总线数据接收方”角色添加到托管标识。

  2. 在“托管标识”选择器中,从系统分配的托管标识类别中选择函数应用。 标签函数应用旁边的括号中可能有一个数字。 该数字表示订阅中有多少具有系统分配标识的应用。

在函数应用中连接到服务总线

  1. 在门户中,搜索函数应用,或在函数应用页上转到该应用。

  2. 应用程序设置中,选择 + 新建,以创建表中的新应用程序设置。 Service BusConnection__fullyQualifiedNamespace <SERVICE_BUS_NAMESPACE>.Service Bus.windows.net

服务主体客户端机密生命周期管理

如果将机密引入跨目录体系结构,则需要管理这些生成的客户端机密的生命周期。 请参阅机密管理的最佳做法,了解如何安全地存储、轮换和监控客户端机密。

本地设置

每个子目录都包含 local.settings.json 文件的存根版本,可对其进行修改以在本地运行 Azure 函数。 若要在 Azure 中配置设置,请更新应用程序设置

DefaultAzureCredential 命令在访问 Azure CLI 凭据之前会枚举多个设置。 为了避免混淆,我们建议在开发本地函数时运行 az login -t <tenant ID> 命令来选择正确的凭据。

贡献者

本文由 Microsoft 维护, 它最初是由以下贡献者撰写的。

主要作者:

后续步骤