用于C++的 Azure 标识客户端库中的凭据链

Azure 标识客户端库提供 凭据 - 派生自 Azure Core 库的 TokenCredential 抽象基类的公共类型。 凭据表示一个独特的身份验证过程,用于从 Microsoft Entra ID 获取访问令牌。 这些凭据可以链接在一起,形成要尝试的有序身份验证机制序列。

链式凭据的工作原理

在运行时,凭据链尝试使用序列的第一个凭据进行身份验证。 如果该凭据无法获取访问令牌,则会尝试序列中的下一个凭据,依此方式,直到成功获取访问令牌。 以下序列图说明了此行为:

显示凭据链序列的图表。

为何使用凭据链?

链式凭证可以提供以下优势:

  • 环境感知:根据应用运行的环境自动选择最合适的凭据。 如果没有它,必须编写如下所示的代码:

    // Set up credential based on environment (Azure or local development)
    std::shared_ptr<Azure::Core::Credentials::TokenCredential> credential;
    if (!std::getenv("WEBSITE_HOSTNAME"))
    {
        credential = std::make_shared<Azure::Identity::AzureCliCredential>();
    }
    else
    {
        credential = std::make_shared<Azure::Identity::ManagedIdentityCredential>();
    }
    
  • 无缝转换:应用可以在不更改身份验证代码的情况下从本地开发迁移到暂存或生产环境。

  • 改进的弹性:包括一种回退机制,当前一个凭据无法获取访问令牌时,该机制会切换到下一个凭据。

如何选择链接凭据

使用C++时,凭据链有两种选择:

  • 使用预配置链:使用由 DefaultAzureCredential 类型实现的预配置链。 有关此方法,请参阅 DefaultAzureCredential 概述部分。
  • 生成自定义凭据链:从空链开始,仅包含所需的内容。 有关此方法,请参阅 ChainedTokenCredential 概述部分。

DefaultAzureCredential 概述

DefaultAzureCredential 是一个固定的预配置凭据链。 它旨在支持许多环境,以及最常见的身份验证流和开发人员工具。 在图形形式中,基础链如下所示:

显示 DefaultAzureCredential 身份验证流的示意图。

DefaultAzureCredential 尝试凭据的顺序如下。

下单 凭证 DESCRIPTION
1 环境 读取环境变量集合,以确定是否为应用配置了应用程序服务主体(应用程序用户)。 如果是这样,DefaultAzureCredential 使用这些值向 Azure 对应用进行身份验证。 此方法最常用于服务器环境,但也可以在本地开发时使用。
2 工作负载标识 如果将应用部署到启用了工作负载标识的 Azure 主机,请对该帐户进行身份验证。
3 Azure CLI 如果开发人员使用 Azure CLI 的 az login 命令向 Azure 进行身份验证,请使用同一帐户向 Azure 验证应用。
4 托管标识 如果应用部署到启用了托管标识的 Azure 主机,请使用该托管标识向 Azure 验证应用。

最简单的形式是,可以使用 DefaultAzureCredential 的无参数版本,如下所示:

#include <azure/identity/default_azure_credential.hpp>
#include <azure/storage/blobs/blob_client.hpp>

int main()
{
    // create a credential
    auto credential = std::make_shared<Azure::Identity::DefaultAzureCredential>();

    // create a Blob service client
    auto blobUrl = "https://<my_account_name>.blob.core.windows.net/mycontainer/myblob";
    Azure::Storage::Blobs::BlobClient blobClient{blobUrl, credential};
}

ChainedTokenCredential 概述

ChainedTokenCredential 是一个空链,可向其添加凭据以满足应用的需求。 例如:

#include <azure/identity/azure_cli_credential.hpp>
#include <azure/identity/chained_token_credential.hpp>
#include <azure/identity/managed_identity_credential.hpp>
#include <azure/storage/blobs/blob_client.hpp>

int main()
{
    // create a credential
    auto credential = std::make_shared<Azure::Identity::ChainedTokenCredential>(
        Azure::Identity::ChainedTokenCredential::Sources{
            std::make_shared<Azure::Identity::AzureCliCredential>(),
            std::make_shared<Azure::Identity::ManagedIdentityCredential>()});

    // create a Blob service client
    auto blobUrl = "https://<my_account_name>.blob.core.windows.net/mycontainer/myblob";
    Azure::Storage::Blobs::BlobClient blobClient{blobUrl, credential};
}

前面的代码示例创建由两个凭据组成的定制凭据链。 首先尝试 AzureCliCredential,然后是 ManagedIdentityCredential(如有必要)。 这条链在图形形式中被表示如下:

此图显示了由 Azure CLI 凭据和托管标识凭据组成的 ChainedTokenCredential 实例的身份验证流。

小窍门

为了提高性能,请优化 ChainedTokenCredential 中凭据的排序,使其按使用频率从高到低排列。

DefaultAzureCredential 的使用指南

DefaultAzureCredential 无疑是开始使用 Azure 标识客户端库的最简单方法,但这种便利性也伴随着一些妥协。 将应用部署到 Azure 后,应了解应用的身份验证要求。 因此,请将 DefaultAzureCredential 替换为特定的 TokenCredential 实现,例如 ManagedIdentityCredential

原因如下:

  • 调试挑战:身份验证失败时,调试和识别违规凭据可能很困难。 必须启用日志记录,才能查看从一个凭据到下一个凭据的进度以及每个凭据的成功/失败状态。 有关详细信息,请参阅调试串联凭据
  • 性能开销:按顺序尝试多个凭据的过程可能会导致性能开销。 例如,在本地开发计算机上运行时,托管标识不可用。 因此,ManagedIdentityCredential 在本地开发环境中始终失败。
  • 不可预知的行为DefaultAzureCredential检查是否存在某些环境变量。 有可能有人可以在主机上的系统级别添加或修改这些环境变量。 这些更改在全局范围内适用,因此会在该计算机上运行的任何应用中改变 DefaultAzureCredential 在运行时的行为。

调试链接凭据

若要诊断意外问题或了解链接凭据正在执行的操作,请在应用中启用日志记录。 为了便于说明,假设使用 DefaultAzureCredential 的无参数形式对 Blob 存储帐户的请求进行身份验证。 应用在本地开发环境中运行,开发人员使用 Azure CLI 向 Azure 进行身份验证。 运行应用程序时,输出中会出现以下相关条目:

DEBUG : Identity: Creating DefaultAzureCredential which combines multiple parameterless credentials into a single one.
DefaultAzureCredential is only recommended for the early stages of development, and not for usage in production environment.
Once the developer focuses on the Credentials and Authentication aspects of their application, DefaultAzureCredential needs to be replaced with the credential that is the better fit for the application.
INFO  : Identity: EnvironmentCredential gets created with ClientSecretCredential.
DEBUG : Identity: EnvironmentCredential: 'AZURE_TENANT_ID', 'AZURE_CLIENT_ID', and 'AZURE_CLIENT_SECRET' environment variables are set, so ClientSecretCredential with corresponding tenantId, clientId, and clientSecret gets created.
WARN  : Identity: Azure Kubernetes environment is not set up for the WorkloadIdentityCredential credential to work.
INFO  : Identity: AzureCliCredential created.
Successful creation does not guarantee further successful token retrieval.
DEBUG : Identity: ManagedIdentityCredential: Environment is not set up for the credential to be created with App Service 2019 source.
DEBUG : Identity: ManagedIdentityCredential: Environment is not set up for the credential to be created with App Service 2017 source.
DEBUG : Identity: ManagedIdentityCredential: Environment is not set up for the credential to be created with Cloud Shell source.
DEBUG : Identity: ManagedIdentityCredential: Environment is not set up for the credential to be created with Azure Arc source.
INFO  : Identity: ManagedIdentityCredential will be created with Azure Instance Metadata Service source.
Successful creation does not guarantee further successful token retrieval.
INFO  : Identity: DefaultAzureCredential: Created with the following credentials: EnvironmentCredential, WorkloadIdentityCredential, AzureCliCredential, ManagedIdentityCredential.
DEBUG : Identity: DefaultAzureCredential: Failed to get token from EnvironmentCredential: GetToken(): error response: 400 Bad Request

{"error":"invalid_grant","error_description":"AADSTS53003: Access has been blocked by Conditional Access policies. The access policy does not allow token issuance. Trace ID: 11223344-5566-7788-9900-aabbccddeeff Correlation ID: ffeeddcc-bbaa-9988-7766-554433221100 Timestamp: 2025-03-07 21:25:44Z","error_codes":[53003],"timestamp":"2025-03-07 21:25:44Z","trace_id":"11223344-5566-7788-9900-aabbccddeeff","correlation_id":"ffeeddcc-bbaa-9988-7766-554433221100","error_uri":"https://login.microsoftonline.com/error?code=53003","suberror":"message_only","claims":"{\"access_token\":{\"capolids\":{\"essential\":true,\"values\":[\"01234567-89ab-cdef-fedc-ba9876543210\"]}}}"}
WARN  : Identity: WorkloadIdentityCredential authentication unavailable. See earlier WorkloadIdentityCredential log messages for details.
DEBUG : Identity: DefaultAzureCredential: Failed to get token from WorkloadIdentityCredential: WorkloadIdentityCredential authentication unavailable. Azure Kubernetes environment is not set up correctly.
INFO  : Identity: DefaultAzureCredential: Successfully got token from AzureCliCredential. This credential will be reused for subsequent calls.
DEBUG : Identity: DefaultAzureCredential: Saved this credential at index 2 for subsequent calls.

在前面的输出中,请注意:

  • EnvironmentCredentialWorkloadIdentityCredential 均未按顺序获取 Microsoft Entra 访问令牌。
  • " AzureCliCredential 成功,如“Successfully got token from AzureCliCredential”开头的条目所示。" 因为 AzureCliCredential 已成功,所以没有尝试其他凭据。