使用 Microsoft Entra 进行跨租户授权

出于安全原因,服务器可能托管在 Azure SignalR 资源的独立租户中。

由于托管标识不能跨租户使用,因此需要在其中注册应用程序 tenantA ,然后将其预配为企业应用程序 tenantB

此文档可帮助你在 tenantA 中创建一个应用程序,并用它来连接到 tenantB 中的 SignalR 资源。

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

第一步是创建多租户应用程序,请参阅:

快速入门:在 Microsoft Entra ID 中注册应用程序

如果已有单个租户应用程序。

在 Microsoft Entra ID 上将单租户应用转换为多租户

有四种帐户类型:

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

创建应用程序时,请务必选择类型 2 或类型 3。

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

记下 应用程序(客户端)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 应用服务器 或其他 SignalR 内置角色取决于你的方案。

    角色 DESCRIPTION 用例
    SignalR 应用服务器 访问服务器连接创建和密钥生成 API。 最常用于在 默认 模式下运行具有 Azure SignalR 资源的应用服务器。
    SignalR 服务所有者 对所有数据平面 API(包括 REST 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. Assign access 到行中,选择“用户、组或服务主体”。
    2. Members 行中,单击 select members,然后选择弹出窗口中的身份。
  7. 适用于 Azure 资源的托管标识。

    1. Assign access 到行中,选择“托管标识”
    2. 在行中 Members ,单击 select members,然后在弹出窗口中选择应用程序。
  8. 选择“下一步”。

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

重要

新添加的角色分配可能需要最多 30 分钟来进行传播。

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

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

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

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

强烈建议使用前 2 种方法发出跨租户请求。

使用证书或客户端机密

  • tenantId 应为 租户 B 的 ID。
  • 两个租户中的 clientId 是相同的。
  • clientSecretclientCert 应在租户 A 中进行配置,请参阅添加凭据

如果不确定自己的租户 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", as it is the recommended value.
        return "TheTokenYouGetFromYourExternalIdentityProvider";
    });

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

使用 SignalR SDK 调试令牌获取可能很困难,因为它取决于令牌结果。 建议先在本地测试令牌获取过程,然后再与 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", as 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 租户

  • audience

    必须为 https://signalr.azure.cn/.default 才能访问 SignalR 资源。

后续步骤

请参阅以下相关文章: