本文介绍如何配置 Microsoft Entra 应用程序以信任托管标识。 然后,可以将托管标识令牌兑换为访问令牌,该访问令牌可以访问 Microsoft Entra 保护的资源,而无需使用或管理应用程序的机密信息。
先决条件
- 具有活动订阅的 Azure 帐户。 免费创建帐户。
- 此 Azure 帐户必须有权 更新应用程序凭据。 以下任何 Microsoft Entra 角色都包括所需的权限:
- 了解 Azure 资源托管标识中的概念。
- 分配给托管工作负荷的 Azure 计算资源(例如虚拟机或 Azure 应用程序服务)的用户分配的托管标识。
- Microsoft Entra ID 中的应用注册。 此应用注册必须属于与托管标识相同的租户
- 如果需要访问另一个租户中的资源,应用注册必须是多租户应用程序,并且已预配到另一个租户中。 了解如何在其他租户中添加多租户应用。
- 应用注册必须将访问权限授予 Microsoft Entra 保护的资源(例如 Azure、Microsoft Graph、Microsoft 365 等)。 可以通过 API 权限或委托权限授予该访问权限。
重要注意事项和限制
若要创建、更新或删除联合标识凭据,执行操作的帐户必须具有应用程序管理员、应用程序开发人员、云应用程序管理员或应用程序所有者角色。 更新联合标识凭据需要 microsoft.directory/applications/credentials/update 权限。
最多可以向应用程序或用户分配的托管标识添加 20 个联合标识凭据。
配置联合标识凭据时,需要提供几条重要信息:
颁发者、 使用者 是设置信任关系所需的关键信息部分。 当 Azure 工作负荷请求 Microsoft 标识平台将托管标识令牌转换为 Entra 应用访问令牌时,会验证联合标识凭据中的颁发者和使用者值是否与托管标识令牌中提供的
issuer
subject
声明相匹配。 如果该验证检查通过,Microsoft 标识平台会向外部软件工作负载发出访问令牌。issuer 是 Microsoft Entra 租户的颁发机构 URL,格式为
https://login.microsoftonline.com/{tenant}/v2.0
。 Microsoft Entra 应用和托管标识都必须属于同一租户。 如果issuer
声明的值中包含前置或后置空格,则会阻止令牌交换。subject
:这是分配给 Azure 工作负荷的托管标识的对象(主体)ID 的区分大小写的 GUID。 托管标识必须与应用注册位于同一租户中,即使目标资源位于不同的云中也是如此。 如果联合标识凭据配置中的subject
与托管标识的主体 ID 完全不匹配,Microsoft 标识平台将拒绝令牌交换。重要说明
只能将用户分配的托管标识用作应用的联合凭据。 不支持系统分配的标识。
audiences 指定托管标识令牌中
aud
声明中的值(必需)。 该值必须是以下值之一,具体取决于目标云。- Microsoft Entra ID 全球服务:
api://AzureADTokenExchange
- 美国政府 Microsoft Entra ID:
api://AzureADTokenExchangeUSGov
- 中国区 Microsoft Entra(由世纪互联运营):
api://AzureADTokenExchangeChina
重要说明
支持访问 另一租户 中的资源。 不支持访问 另一个云 中的资源。 对其他云的令牌请求将失败。
重要说明
如果您在 issuer、subject 或 audience 设置中不小心添加了错误信息,联合标识凭据仍会被成功创建,而且不会报错。 在令牌交换失败之前,此错误不会变得明显。
- Microsoft Entra ID 全球服务:
“name”是联合标识凭据的唯一标识符。 (必需)此字段的字符限制为 3-120 个字符,并且必须对 URL 友好。 支持字母数字、短划线或下划线字符,第一个字符必须是字母数字字符。 它在创建后就不可变。
描述 是用户提供的联合身份凭证描述(可选)。 该说明未经过 Microsoft Entra ID 的验证或检查。 此字段的字符数限制为 600 个字符。
不支持在任何联合身份凭据属性值中使用通配符。
在应用程序上配置联合标识凭据
在本部分,你将在现有应用程序上配置联合标识凭据,以信任托管标识。 使用以下选项卡选择如何在现有应用程序上配置联合标识凭据。
登录 Microsoft Entra 管理中心。 确认你位于注册应用程序的租户中。
浏览到 身份>应用>应用注册,并在主窗口中选择你的应用程序。
在“管理”下,选择“证书和机密”。
选择“联合凭据”选项卡,然后选择 添加凭据。
在联合身份验证凭据方案下拉列表中,选择托管标识,并根据下表填写值:
字段 说明 示例 发行人 用于颁发托管标识令牌的 Microsoft Entra ID 颁发机构的 OAuth 2.0/OIDC 颁发者 URL。 该值将自动使用当前 Entra 租户颁发者填充。 https://login.microsoftonline.com/{tenantID}/v2.0
选择“托管标识” 单击此链接以选择将充当联合标识凭据的托管标识。 只能将用户分配的托管标识用作凭据。 msi-webapp1 说明(可选) 用户提供的联合标识凭据的说明。 信任工作负荷 UAMI 作为我的应用的凭据 读者 必须在外部令牌中显示的访问群体值。 必须设置为以下值之一:
• Entra ID 全局服务:api://AzureADTokenExchange
• 美国政府 Entra ID:api://AzureADTokenExchangeUSGov
• 由世纪互联运营的中国区 Entra ID:api://AzureADTokenExchangeChina
更新应用程序代码以请求访问令牌
以下代码片段演示如何获取托管标识令牌并将其用作 Entra 应用程序的凭据。 在目标资源位于与 Entra 应用程序相同租户或不同租户的两种情况下,示例均有效。
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.
// Entra ID Global cloud: api://AzureADTokenExchange
// Entra ID US Government: api://AzureADTokenExchangeUSGov
// Entra ID China operated by 21Vianet: api://AzureADTokenExchangeChina
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.windows.net/{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: {blobClient.Name}, uri: {blobClient.Uri}");
}
}
}
Microsoft.Identity.Web
在 Microsoft.Identity.Web 中,可以在 appsettings.json 中设置 ClientCredentials
部分,以便使用 SignedAssertionFromManagedIdentity
来启用代码使用配置的托管标识作为凭据:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"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 类来获取托管标识令牌。 然后可以在构造机密客户端应用程序时将此令牌用作客户端断言。
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.microsoftonline.com/{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.windows.net/.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.windows.net/{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}");
}
}
}