使用 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 中配置了参数 clientSecret 和 clientCert 参数。有关详细信息,请参阅 “添加凭据”。

如果不确定自己的租户 ID,请参阅查找 Microsoft Entra 租户

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

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

使用联合标识

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

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

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

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

    option.Endpoints = [
        new ServiceEndpoint(new Uri(), "https://<resource>.signalr.azure.cn"), 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://AzureADTokenExchangeChina" because it is the recommended value.
        return "TheTokenYouGetFromYourExternalIdentityProvider";
    });

    option.Endpoints = [
        new ServiceEndpoint(new Uri(), "https://<resource>.signalr.azure.cn"), 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://AzureADTokenExchangeChina" because it is the recommended value.
    return TheTokenYouGetFromYourExternalIdentityProvider;
});

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

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

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

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