将应用程序配置为信任托管标识(预览版)

本文介绍如何将 Microsoft Entra 应用程序配置为信任托管标识。 然后,可以为访问令牌交换托管标识令牌,该令牌可以访问 Microsoft Entra 保护的资源,而无需使用或管理应用机密。

先决条件

重要注意事项和限制

若要创建、更新或删除联合标识凭据,则执行操作的帐户必须具有应用程序管理员应用程序开发人员云应用程序管理员或应用程序所有者角色。 更新联合标识凭据需要 microsoft.directory/applications/credentials/update 权限

最多可以向应用程序或用户分配的托管标识添加 20 个联合标识凭据。

配置联合标识凭据时,需要提供几条重要信息:

  • issuer 和 subject 是建立信任关系所需的关键信息。 issuersubject 的组合在应用中必须唯一。 当 Azure 工作负载请求 Microsoft 标识平台来交换访问令牌的托管标识令牌时,将针对托管标识令牌中提供的 issuersubject 声明来检查联合标识凭据的 issuer 和 subject 值。 如果该验证检查通过,Microsoft 标识平台会向外部软件工作负荷发出访问令牌。

  • issuer 是 Microsoft Entra 租户的颁发机构 URL,格式为 https://login.partner.microsoftonline.cn/{tenant}/v2.0。 Microsoft Entra 应用与托管标识必须属于同一租户。 如果 issuer 声明具有值中的前导或尾随空格,则会阻止令牌交换。

    重要

    尽管应用注册与托管标识必须位于同一租户中,但应用注册的服务主体仍可以兑换托管标识令牌。

  • subject 是分配给 Azure 工作负载的托管标识对象 ID(主体 ID)的 GUID。 Microsoft 标识平台会查看传入的外部令牌,如果联合标识凭据中配置的 subject 字段与托管标识的主体 ID 不匹配,则拒绝交换访问令牌。 GUID 区分大小写。

  • 重要

    只能在此功能中使用用户分配的托管标识。

  • *audiences 列出了可出现在外部令牌中的受众(必需)。 必须添加一个受众值,该值限制为 600 个字符。 该值必须是下列值之一,并且必须与托管标识令牌中 aud 声明的值相匹配。

    • 公有云api://AzureADTokenExchange
    • Fairfaxapi://AzureADTokenExchangeUSGov
    • Mooncakeapi://AzureADTokenExchangeChina
    • UsNatapi://AzureADTokenExchangeUSNat
    • USSecapi://AzureADTokenExchangeUSSec

    重要

    如果在 issuer、subject 或 audience 设置中意外添加了不正确的信息,则已成功创建联合标识凭据,且未出现错误。 在令牌交换失败之前,此错误不会变得明显。

  • name 是联合标识凭据的唯一标识符。 (必需)此字段的字符限制为 3-120 个字符,并且必须对 URL 友好。 支持字母数字、短划线或下划线字符,并且第一个字符必须是字母数字字符。 它在创建后就不可变。

  • description 是用户提供的联合标识凭据的说明(可选)。 Microsoft Entra ID 不会验证或检查说明。 此字段的上限为 600 个字符。

任何联合标识凭据属性值都不支持通配符。

记下托管标识的对象 ID

  1. 登录到 Azure 门户
  2. 在搜索框中输入“托管标识”。 在“服务”下选择“托管标识”
  3. 搜索并选择你创建的用户分配的托管标识作为先决条件
  4. 在“概述”窗格中,复制“对象(主体)ID”值。 此值用作联合凭据配置中的 subject 字段。

Azure 门户中用户分配的托管标识的屏幕截图。该对象 ID 突出显示,将用作联合凭据配置中的 *subject* 字段。

在现有应用程序上配置联合标识凭据

在本部分中,你将在现有应用程序上配置联合标识凭据,以信任托管标识。 使用以下选项卡来选择如何在现有应用程序中配置联合标识凭据。

  1. 登录 Microsoft Entra 管理中心。 检查你是否已在注册应用程序的租户中。

  2. 浏览到“标识”>“应用程序”>“应用注册”,然后在主窗口中选择你的应用程序。

  3. 在“管理”下,选择“证书和机密”

  4. 选择“联合凭据”选项卡,选择“添加凭据”

    Microsoft Entra 管理中心的证书与机密窗格的屏幕截图,其中突出显示了联合凭据选项卡。

  5. 在“联合凭据方案”下拉列表中,选择“其他颁发者”,并根据下表填写值:

    字段 说明 示例
    颁发者 Microsoft Entra ID 颁发机构的 OAuth 2.0/OIDC 颁发者 URL。 https://login.partner.microsoftonline.cn/{tenantID}/v2.0
    使用者标识符 托管标识的 Principal ID GUID。 aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb
    名称 凭据的唯一描述性名称。 msi-webapp1
    说明(可选) 用户提供的联合标识凭据的说明。 信任工作负载 UAMI 来模拟应用
    受众 必须在外部令牌中显示的受众值。 公有云api://AzureADTokenExchange
    Fairfaxapi://AzureADTokenExchangeUSGov
    Mooncakeapi://AzureADTokenExchangeChina
    USNatapi://AzureADTokenExchangeUSNat
    USSecapi://AzureADTokenExchangeUSSec

    Microsoft Entra 管理中心内凭证屏幕的屏幕截图。

更新应用程序代码,以请求访问令牌

下表中的以下代码示例显示客户端凭据“服务到服务”流。 但是,托管标识作为凭据,可用于其他身份验证流,例如代表 (OBO) 流。 在资源租户与应用注册和托管标识位于同一租户或不同租户的情况下,示例均有效。

Azure.Identity

以下示例演示如何使用 Azure.Identity 连接到 Azure 存储容器,但可改编为访问受 Microsoft Entra 保护的任何资源。 在资源租户与应用注册和托管标识位于同一租户或不同租户的情况下,示例均有效。

using Azure.Identity;
using Azure.Storage.Blobs;

internal class Program
{
    // This example demonstrates how to access an Azure blob storage account by utilizing the manage identity credential.
  static void Main(string[] args)
  {
        string storageAccountName = "YOUR_STORAGE_ACCOUNT_NAME";
        string containerName = "CONTAINER_NAME";
        
        // The application must be granted access on the target resource
      string appClientId = "YOUR_APP_CLIENT_ID";

        // The tenant where the target resource is created, in this example, the storage account tenant
        // If the resource tenant different from the app tenant, your app needs to be 
      string resourceTenantId = "YOUR_RESOURCE_TENANT_ID";

        // The managed identity which you configured as a Federated Identity Credential (FIC)
      string miClientId = "YOUR_MANAGED_IDENTITY_CLIENT_ID"; 

        // Audience value must be one of the below values depending on the target cloud.
        // Public cloud: api://AzureADTokenExchange
        //  Fairfax: api://AzureADTokenExchangeUSGov
        //  Mooncake: api://AzureADTokenExchangeChina
        //  USNat: api://AzureADTokenExchangeUSNat
        //  USSec: api://AzureADTokenExchangeUSSec
      string audience = "api://AzureADTokenExchange";

        // 1. Create an assertion with the managed identity access token, so that it can be exchanged an app token
        var miCredential = new ManagedIdentityCredential(managedIdentityClientId);
        ClientAssertionCredential assertion = new(
            tenantId,
            appClientId,
            async (token) =>
            {
                // fetch Managed Identity token for the specified audience
                var tokenRequestContext = new Azure.Core.TokenRequestContext(new[] { $"{audience}/.default" });
                var accessToken = await miCredential.GetTokenAsync(tokenRequestContext).ConfigureAwait(false);
                return accessToken.Token;
            });

        // 2. The assertion can be used to obtain an App token (taken care of by the SDK)
        var containerClient  = new BlobContainerClient(new Uri($"https://{storageAccountName}.blob.core.chinacloudapi.cn/{containerName}"), assertion);

        await foreach (BlobItem blob in containerClient.GetBlobsAsync())
        {
            // TODO: perform operations with the blobs
            BlobClient blobClient = containerClient.GetBlobClient(blob.Name);
            Console.WriteLine($"Blob name: {blobClent.Name}, uri: {blobClient.Uri}");            
        }
  }
}

Microsoft.Identity.Web

在 Microsoft.Identity.Web 中,Web 应用程序或 Web API 可将客户端证书替换为签名的客户端断言以进行身份验证。 在应用程序中,可将 appsettings.json 中的 ClientCredentials 部分更新为以下配置:

{
  "AzureAd": {
    "Instance": "https://login.partner.microsoftonline.cn/",
    "ClientId": "YOUR_APPLICATION_ID",
    "TenantId": "YOUR_TENANT_ID",

   "ClientCredentials": [
      {
        "SourceType": "SignedAssertionFromManagedIdentity",
        "ManagedIdentityClientId": "YOUR_USER_ASSIGNED_MANAGED_IDENTITY_CLIENT_ID",
        "TokenExchangeUrl":"api://AzureADTokenExchange"
      }
   ]
  }
}

MSAL (.NET)

在 MSAL 中,可以使用 ManagedClientApplication 类获取托管标识令牌。 然后,在构造机密客户端应用程序时,可将此令牌用作客户端断言。

警告

对于 .NET 应用,我们强烈建议使用基于 MSAL 的高级资料库,例如 Microsoft.Identity.WebAzure.Identity

using Microsoft.Identity.Client;
using Azure.Storage.Blobs;
using Azure.Core;

internal class Program
{
  static async Task Main(string[] args)
  {
        string storageAccountName = "YOUR_STORAGE_ACCOUNT_NAME";
        string containerName = "CONTAINER_NAME";

      string appClientId = "YOUR_APP_CLIENT_ID";
      string resourceTenantId = "YOUR_RESOURCE_TENANT_ID";
      Uri authorityUri = new($"https://login.partner.microsoftonline.cn/{resourceTenantId}");
      string miClientId = "YOUR_MI_CLIENT_ID";
      string audience = "api://AzureADTokenExchange";

      // Get mi token to use as assertion
      var miAssertionProvider = async (AssertionRequestOptions _) =>
      {
            var miApplication = ManagedIdentityApplicationBuilder
                .Create(ManagedIdentityId.WithUserAssignedClientId(miClientId))
                .Build();

            var miResult = await miApplication.AcquireTokenForManagedIdentity(audience)
                .ExecuteAsync()
                .ConfigureAwait(false);
            return miResult.AccessToken;
      };

      // Create a confidential client application with the assertion.
      IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(appClientId)
        .WithAuthority(authorityUri, false)
        .WithClientAssertion(miAssertionProvider)
        .WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
        .Build();

        // Get the federated app token for the storage account
      string[] scopes = [$"https://{storageAccountName}.blob.core.chinacloudapi.cn/.default"];
      AuthenticationResult result = await app.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false);

        TokenCredential tokenCredential = new AccessTokenCredential(result.AccessToken);
        var client = new BlobContainerClient(
            new Uri($"https://{storageAccountName}.blob.core.chinacloudapi.cn/{containerName}"),
            tokenCredential);

        await foreach (BlobItem blob in containerClient.GetBlobsAsync())
        {
            // TODO: perform operations with the blobs
            BlobClient blobClient = containerClient.GetBlobClient(blob.Name);
            Console.WriteLine($"Blob name: {blobClient.Name}, URI: {blobClient.Uri}");
        }
  }
}

另请参阅