使用 Microsoft Entra 配置跨租户授权

出于安全原因,服务器可能托管在独立于 Azure SignalR 服务资源的租户中。 由于托管标识不能跨租户使用,因此需要在租户 A 中注册应用程序,然后在租户 B 中将其预配为企业应用程序。本文介绍如何在租户 A 中创建应用程序,并使用它连接到租户 B 中的 Azure SignalR 服务资源。

在租户 A 中注册多租户应用程序

第一步是创建多租户应用程序。 有关详细信息,请参阅 快速入门:在 Microsoft Entra ID 中注册应用程序

如果已有单个租户应用程序,请按照 Microsoft Entra ID 上的“将单租户应用转换为多租户”中的说明进行作。

有四种帐户类型:

  • 此组织目录中的帐户
  • 任何组织目录中的帐户
  • 任何组织目录中的帐户和个人Microsoft帐户
  • 个人Microsoft帐户

创建应用程序时,请务必选择第二种类型或第三种类型。

显示已注册应用程序的信息的概述的屏幕截图

记下应用程序(客户端)ID 和目录(租户)ID,以便在以下步骤中使用。

在租户 B 中预配应用程序

不能将角色分配给在其他租户中注册的应用程序。 必须在租户 B 中将其预配为外部企业应用程序。如果需要更多信息,可以了解 应用注册和企业应用程序之间的差异

简言之,企业应用程序是服务主体,应用注册不是。 企业应用程序从应用程序对象继承某些属性,例如应用程序(客户端)ID。

在注册应用的租户中创建默认服务主体。 对于其他租户,需要预配应用以获取企业应用程序服务主体。 有关详细信息,请参阅 Microsoft Entra ID 中的多租户应用程序创建企业应用程序

不同租户中的企业应用程序具有不同的目录(租户)ID,但它们共享相同的应用程序(客户端)ID。

将角色分配给企业应用程序

在租户 B 中预配企业应用程序后,可以向其分配角色。

以下步骤介绍如何将 SignalR 应用服务器角色分配给 Azure SignalR 服务资源的服务主体或托管标识。 有关详细步骤,请参阅使用 Azure 门户分配 Azure 角色

注释

可以将角色分配给任何范围,包括管理组、订阅、资源组或单个资源。 若要了解有关范围的详细信息,请参阅了解 Azure RBAC 的作用域

  1. Azure 门户中,转到你的 Azure SignalR 服务资源。

  2. 在左侧窗格中,选择访问控制 (IAM)

  3. 选择添加>添加角色分配

    屏幕截图显示了用于添加角色分配的访问控制和选择页面。

  4. 在“角色”选项卡上,选择“SignalR 应用服务器”。 其他 Azure SignalR 服务内置角色取决于你的方案。

    角色 DESCRIPTION 用例
    SignalR 应用服务器 访问创建服务器连接和生成密钥的 API。 最常用于在默认模式下运行 Azure SignalR 资源的应用服务器。
    SignalR 服务所有者 完全访问所有数据平面 API,包括 REST API、创建服务器连接的 API 以及生成密钥/令牌的 API。 用于在“无服务器”模式下运行 Azure SignalR 服务资源的协商服务器。 它需要 REST API 权限和身份验证 API 权限。
    SignalR REST API 所有者 对数据平面 REST API 的完全访问。 用于 Azure SignalR 管理 SDK 来管理连接和组,但它 进行服务器连接或处理协商请求。
    SignalR REST API 读者 对数据平面 REST API 的只读访问。 编写调用只读 REST API 的监视工具时使用。
  5. 选择“下一步”。

  6. 对于 Microsoft Entra 应用程序:

    1. 分配访问权限一行中,选择用户、组或服务主体
    2. 在“成员”行中,选择“选择成员”,然后在弹出窗口中选择身份。
  7. 对于 Azure 资源的托管标识,请执行以下操作:

    1. 在“将访问权限分配给”行中,选择“托管标识”。
    2. 在“ 成员 ”行中, 选择成员,然后在弹出窗口中选择应用程序。
  8. 选择“下一步”。

  9. 查看分配,然后选择 “查看 + 分配 ”以确认角色分配。

重要

新添加的角色分配可能需要长达 30 分钟的时间来进行传播。

若要详细了解如何分配和管理 Azure 角色,请参阅:

将 Azure SignalR 服务 SDK 配置为使用企业应用程序

应用程序使用三种不同类型的凭据对自身进行身份验证:

  • 证书
  • 客户端机密
  • 联合标识

我们强烈建议您使用证书或客户端密钥来执行跨租户请求。

使用证书或客户端机密

  • 参数 tenantId 是租户 B 的 ID。
  • 在这两个租户中,clientId参数是相等的。
  • 租户 A 中配置了参数 clientSecretclientCert 参数。有关详细信息,请参阅 “添加凭据”。

如果不确定租户 ID,请参阅 “查找Microsoft Entra 租户”。

services.AddSignalR().AddAzureSignalR(option =>
{
    var credential1 = new ClientSecretCredential("tenantId", "clientId", "clientSecret");
    var credential2 = new ClientCertificateCredential("tenantId", "clientId", "path-to-cert");

    option.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint(new Uri("https://<resource1>.service.signalr.net"), credential1),
        new ServiceEndpoint(new Uri("https://<resource2>.service.signalr.net"), credential2),
    };
});

使用联合标识

出于安全原因,订阅中可能会禁用证书和客户端机密。 在这种情况下,您需要使用外部身份提供者,或者尝试托管身份的预览支持。 有关详细信息,请参见:

有关详细信息和视频指南,请参阅Microsoft Entra 跨租户应用程序联合标识凭据(FIC)。

将托管标识用作标识提供者时,代码如以下示例所示:

  • 参数 tenantId 是租户 B 的 ID。
  • 两个租户中的 clientId 参数相等。
services.AddSignalR().AddAzureSignalR(option =>
{
    var msiCredential = new ManagedIdentityCredential("msiClientId");

    var credential = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) =>
    {
        // Entra ID US Government: api://AzureADTokenExchangeUSGov
        // Entra ID China operated by 21Vianet: api://AzureADTokenExchangeChina
        var request = new TokenRequestContext([$"api://AzureADTokenExchange/.default"]);
        var response = await msiCredential.GetTokenAsync(request, ctoken).ConfigureAwait(false);
        return response.Token;
    });

    option.Endpoints = [
        new ServiceEndpoint(new Uri(), "https://<resource>.service.signalr.net"), credential);
    ];
});

使用外部标识提供者时,代码如以下示例所示:

services.AddSignalR().AddAzureSignalR(option =>
{
    var credential = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) =>
    {
        // Find your own way to get a token from the external identity provider.
        // The audience of the token should be "api://AzureADTokenExchange" because it is the recommended value.
        return "TheTokenYouGetFromYourExternalIdentityProvider";
    });

    option.Endpoints = [
        new ServiceEndpoint(new Uri(), "https://<resource>.service.signalr.net"), credential);
    ];
});

使用 Azure SignalR 服务 SDK 调试令牌获取是一项挑战,因为它取决于令牌结果。 建议先在本地测试令牌获取过程,然后再与 Azure SignalR 服务 SDK 集成。

var assertion = new ClientAssertionCredential("tenantId", "appClientId", async (ctoken) =>
{
    // Find your own way to get a token from the external identity provider.
    // The audience of the token should be "api://AzureADTokenExchange" because it is the recommended value.
    return TheTokenYouGetFromYourExternalIdentityProvider;
});

var request = new TokenRequestContext(["https://signalr.azure.com/.default");
var token = await assertion.GetTokenAsync(assertion);
Console.log(token.Token);

关键点是使用内部凭据从api://AzureADTokenExchange或其他受信任的标识平台获取clientAssertion参数。 然后使用它与 https://signalr.azure.com/.default 受众交换令牌来访问资源。

目标是获取具有以下声明的令牌。 使用 jwt.io 来帮助你解码令牌:

  • oid:该值应等于企业应用程序对象 ID。 如果不知道在何处获取它,请参阅 “检索企业对象 ID”。
  • tid:该值应等于租户 B 的目录 ID。如果不确定租户 ID,请参阅 “查找Microsoft Entra 租户”。
  • 用户:用户必须是 https://signalr.azure.com/.default 才能访问 Azure SignalR 服务资源。