在不处理凭据的情况下从应用程序连接到资源

支持托管标识的 Azure 资源始终提供一个选项,可用于指定托管标识来连接到支持 Microsoft Entra 身份验证的 Azure 资源。 托管标识支持使得开发人员无需在代码中管理凭据。 使用支持托管标识的 Azure 资源时,托管标识是推荐的验证选项。 阅读托管标识概述

本页演示如何配置应用服务,以便它可以连接到 Azure Key Vault、Azure 存储和 Microsoft SQL Server。 这些原则可用于支持托管标识以及将连接到支持 Microsoft Entra 身份验证的资源的任何 Azure 资源。

代码示例使用 Azure 标识客户端库,这是建议的方法,因为它会自动处理许多步骤,包括获取连接中使用的访问令牌。

托管标识可以连接到哪些资源?

托管标识可以连接到支持 Microsoft Entra 身份验证的任何资源。 一般情况下,资源无需特殊支持即可允许托管标识连接到它。

某些资源不支持 Microsoft Entra 身份验证,或者其客户端库不支持使用令牌进行身份验证。 请继续阅读,了解如何使用托管标识安全地访问凭据,而无需将其存储在代码或应用程序配置中。

创建托管标识

两种类型的托管标识:系统分配的托管标识和用户分配的托管标识。 系统分配的标识直接链接到单个 Azure 资源。 删除 Azure 资源时也会删除标识。 用户分配的托管标识可以与多个 Azure 资源相关联,其生命周期独立于这些资源。

在大多数情况下,建议使用用户分配的托管标识。 如果使用的源资源不支持用户分配的托管标识,则应参考该资源提供程序的文档,了解如何将其配置为具有系统分配的托管标识。

重要

用于创建托管标识的帐户需要“托管标识参与者”等角色来创建新的用户分配的托管标识。

使用首选选项创建用户分配的托管标识:

创建用户分配的托管标识后,请记下创建托管标识时返回的 clientIdprincipalId 值。 在添加权限时将使用 principalId,应用程序代码中将使用 clientId

为应用服务配置用户分配的托管标识

在代码中使用托管标识之前,必须将其分配给将使用它的应用服务。 配置应用服务以使用用户分配的托管标识的过程要求在应用配置中指定托管标识的资源标识符

向标识添加权限

将应用服务配置为使用用户分配的托管标识后,请向该标识授予必要的权限。 在此应用场景中,我们使用此标识与 Azure 存储进行交互,因此你需要使用 Azure 基于角色的访问控制 (RBAC) 系统向用户分配的托管标识授予对资源的权限。

重要

需要目标资源的“用户访问管理员”或“所有者”等角色才能添加角色分配。 确保向应用程序授予运行所需的最低权限。

要访问的任何资源都需要你授予标识权限。 例如,如果你请求令牌以访问 Key Vault,则还必须添加一个访问策略,其中包括你的应用或 Functions 的托管身份。 否则,对 Key Vault 的调用将被拒绝,即使你使用了有效令牌。 Azure SQL 数据库也是如此。 若详细了解支持 Microsoft Entra 令牌的资源,请参阅“支持 Microsoft Entra 身份验证的 Azure 服务”。

在代码中使用托管标识

完成上述步骤后,应用服务将具有一个托管标识且该托管标识对 Azure 资源具有相应权限。 可以使用托管标识获取访问令牌,代码可以使用该令牌与 Azure 资源进行交互,而不是将凭据存储在代码中。

建议将 Azure 标识库用于首选编程语言。 库会为你获取访问令牌,使连接到目标资源变得简单。

详细了解以下 Azure 标识库:

在开发环境中使用 Azure 标识库

每个 Azure 标识库都会提供一种 DefaultAzureCredential 类型。 DefaultAzureCredential 自动尝试通过多种机制进行身份验证,包括环境变量或交互式登录。 可以使用自己的凭据在开发环境中使用凭据类型。 还可以使用托管标识在生产 Azure 环境中使用它。 部署应用程序时无需更改代码。

如果使用用户分配的托管标识,还应通过将标识的客户端 ID 作为参数传入来显式指定要进行身份验证的用户分配的托管标识。 可以通过浏览 Azure 门户中的标识来检索客户端 ID。

访问 Azure 存储中的 Blob

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

// code omitted for brevity

// Specify the Client ID if using user-assigned managed identities
var clientID = Environment.GetEnvironmentVariable("Managed_Identity_Client_ID");
var credentialOptions = new DefaultAzureCredentialOptions
{
    ManagedIdentityClientId = clientID
};
var credential = new DefaultAzureCredential(credentialOptions);                        

var blobServiceClient1 = new BlobServiceClient(new Uri("<URI of Storage account>"), credential);
BlobContainerClient containerClient1 = blobServiceClient1.GetBlobContainerClient("<name of blob>");
BlobClient blobClient1 = containerClient1.GetBlobClient("<name of file>");

if (blobClient1.Exists())
{
    var downloadedBlob = blobClient1.Download();
    string blobContents = downloadedBlob.Value.Content.ToString();                
}

访问 Azure Key Vault 中存储的机密

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Azure.Core;

// code omitted for brevity

// Specify the Client ID if using user-assigned managed identities
var clientID = Environment.GetEnvironmentVariable("Managed_Identity_Client_ID");
var credentialOptions = new DefaultAzureCredentialOptions
{
    ManagedIdentityClientId = clientID
};
var credential = new DefaultAzureCredential(credentialOptions);        

var client = new SecretClient(
    new Uri("https://<your-unique-key-vault-name>.vault.azure.cn/"),
    credential);
    
KeyVaultSecret secret = client.GetSecret("<my secret>");
string secretValue = secret.Value;

访问 Azure SQL 数据库

using Azure.Identity;
using Microsoft.Data.SqlClient;

// code omitted for brevity

// Specify the Client ID if using user-assigned managed identities
var clientID = Environment.GetEnvironmentVariable("Managed_Identity_Client_ID");
var credentialOptions = new DefaultAzureCredentialOptions
{
    ManagedIdentityClientId = clientID
};

AccessToken accessToken = await new DefaultAzureCredential(credentialOptions).GetTokenAsync(
    new TokenRequestContext(new string[] { "https://database.chinacloudapi.cn//.default" }));                        

using var connection = new SqlConnection("Server=<DB Server>; Database=<DB Name>;")
{
    AccessToken = accessToken.Token
};
var cmd = new SqlCommand("select top 1 ColumnName from TableName", connection);
await connection.OpenAsync();
SqlDataReader dr = cmd.ExecuteReader();
while(dr.Read())
{
    Console.WriteLine(dr.GetValue(0).ToString());
}
dr.Close();	

连接到库中不支持 Microsoft Entra ID 或不支持基于令牌进行身份验证的资源

某些 Azure 资源尚不支持 Microsoft Entra 身份验证,或者其客户端库不支持使用令牌进行身份验证。 通常,这些资源是开放源代码技术,需要用户名和密码或连接字符串中的访问密钥。

为了避免在代码或应用程序配置中存储凭据,可以将凭据作为机密存储在 Azure Key Vault 中。 使用上面显示的示例,可以使用托管标识从 Azure KeyVault 检索机密,并将凭据传递到连接字符串中。 此方法意味着无需在代码或环境中直接处理凭据。

直接处理令牌的指南

在某些情况下,可能需要手动获取托管标识的令牌,而不是使用内置方法连接到目标资源。 这些方案不包含你正在使用的编程语言的客户端库或要连接到的目标资源,或者连接到未在 Azure 上运行的资源。 手动获取令牌时,我们提供了以下准则:

缓存获取的令牌

为了提高性能和可靠性,我们建议应用程序将令牌缓存在本地内存中,或者如果要将其保存到磁盘,则加密。 由于托管标识令牌有效期为 24 小时,因此定期请求新令牌没有好处,因为缓存的令牌将从令牌颁发终结点返回。 如果超出请求限制,将受到速率限制并收到 HTTP 429 错误。

获取令牌时,可以将令牌缓存设置为在生成令牌时返回的 expires_on(或等效属性)前 5 分钟过期。

令牌检查

应用程序不应依赖于令牌的内容。 令牌的内容仅适用于正在访问的受众(目标资源),而不是请求令牌的客户端。 令牌内容可能会在将来更改或加密。

不要公开或移动令牌

令牌应被视为凭据。 不要向用户或其他服务公开令牌;例如,日志记录/监视解决方案。 除了针对目标资源进行身份验证外,不应从使用令牌的源资源移动令牌。

后续步骤