ASP.NET Core 中的 Azure Key Vault 配置提供程序Azure Key Vault Configuration Provider in ASP.NET Core

作者: Luke LathamAndrew StantonBy Luke Latham and Andrew Stanton-Nurse

本文档说明如何使用Microsoft Azure Key Vault配置提供程序从 Azure Key Vault 机密加载应用配置值。This document explains how to use the Microsoft Azure Key Vault Configuration Provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault 是一项基于云的服务,可帮助保护应用程序和服务使用的加密密钥和机密。Azure Key Vault is a cloud-based service that assists in safeguarding cryptographic keys and secrets used by apps and services. 将 Azure Key Vault 用于 ASP.NET Core 应用的常见方案包括:Common scenarios for using Azure Key Vault with ASP.NET Core apps include:

  • 控制对敏感配置数据的访问。Controlling access to sensitive configuration data.
  • 在存储配置数据时满足 FIPS 140-2 级别2验证的硬件安全模块(HSM)的要求。Meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSM's) when storing configuration data.

查看或下载示例代码如何下载View or download sample code (how to download)

Packages

将包引用添加到AzureKeyVault包。Add a package reference to the Microsoft.Extensions.Configuration.AzureKeyVault package.

示例应用Sample app

该示例应用在Program.cs文件顶部的 #define 语句确定的两种模式中运行:The sample app runs in either of two modes determined by the #define statement at the top of the Program.cs file:

  • Certificate – 演示了如何使用 Azure Key Vault 的客户端 ID 和 x.509 证书来访问存储在 Azure Key Vault 中的机密。Certificate – Demonstrates the use of an Azure Key Vault Client ID and X.509 certificate to access secrets stored in Azure Key Vault. 可以从部署到 Azure App Service 的任何位置运行此版本的示例,也可以从部署到 ASP.NET Core 应用程序的任何主机上运行。This version of the sample can be run from any location, deployed to Azure App Service or any host capable of serving an ASP.NET Core app.
  • Managed – 演示了如何使用Azure 资源的托管标识对应用进行身份验证,以使用 Azure AD 身份验证 Azure Key Vault,而不会在应用的代码或配置中存储凭据。Managed – Demonstrates how to use Managed identities for Azure resources to authenticate the app to Azure Key Vault with Azure AD authentication without credentials stored in the app's code or configuration. 使用托管标识进行身份验证时,不需要 Azure AD 的应用程序 ID 和密码(客户端机密)。When using managed identities to authenticate, an Azure AD Application ID and Password (Client Secret) aren't required. 必须将示例的 Managed 版本部署到 Azure。The Managed version of the sample must be deployed to Azure. 按照使用 Azure 资源的托管标识部分中的指导进行操作。Follow the guidance in the Use the Managed identities for Azure resources section.

有关如何使用预处理器指令(#define)配置示例应用的详细信息,请参阅 ASP.NET Core 简介For more information on how to configure a sample app using preprocessor directives (#define), see ASP.NET Core 简介.

开发环境中的机密存储Secret storage in the Development environment

使用机密管理器工具在本地设置机密。Set secrets locally using the Secret Manager tool. 当在开发环境中的本地计算机上运行示例应用时,会从本地密钥管理器存储中加载密码。When the sample app runs on the local machine in the Development environment, secrets are loaded from the local Secret Manager store.

机密管理器工具需要应用的项目文件中的 <UserSecretsId> 属性。The Secret Manager tool requires a <UserSecretsId> property in the app's project file. 将属性值({GUID})设置为任何唯一 GUID:Set the property value ({GUID}) to any unique GUID:

<PropertyGroup>
  <UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>

机密以名称-值对的形式创建。Secrets are created as name-value pairs. 分层值(配置节)使用 : (冒号)作为ASP.NET Core 配置项名称中的分隔符。Hierarchical values (configuration sections) use a : (colon) as a separator in ASP.NET Core configuration key names.

机密管理器是从打开到项目内容根目录的命令 shell 使用的,其中 {SECRET NAME} 是名称,{SECRET VALUE} 是值:The Secret Manager is used from a command shell opened to the project's content root, where {SECRET NAME} is the name and {SECRET VALUE} is the value:

dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"

从项目的内容根在命令外壳中执行以下命令,为示例应用设置机密:Execute the following commands in a command shell from the project's content root to set the secrets for the sample app:

dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"

如果这些机密存储在生产环境中的机密存储 Azure Key Vault 中,并具有 Azure Key Vault部分,则 _dev 后缀将更改为 _prodWhen these secrets are stored in Azure Key Vault in the Secret storage in the Production environment with Azure Key Vault section, the _dev suffix is changed to _prod. 后缀在应用的输出中提供视觉提示,指示配置值的源。The suffix provides a visual cue in the app's output indicating the source of the configuration values.

Azure Key Vault 中的生产环境中的机密存储Secret storage in the Production environment with Azure Key Vault

快速入门:使用 Azure CLI 主题的 Azure Key Vault 设置和检索机密的说明,请参阅此处,以创建 Azure Key Vault 和存储示例应用使用的机密。The instructions provided by the Quickstart: Set and retrieve a secret from Azure Key Vault using Azure CLI topic are summarized here for creating an Azure Key Vault and storing secrets used by the sample app. 有关更多详细信息,请参阅主题。Refer to the topic for further details.

  1. 使用Azure 门户中的以下方法之一打开 Azure Cloud shell:Open Azure Cloud shell using any one of the following methods in the Azure portal:

    • 选择代码块右上角的“试用”。Select Try It in the upper-right corner of a code block. 在文本框中使用搜索字符串 "Azure CLI"。Use the search string "Azure CLI" in the text box.
    • 在浏览器中打开 Cloud Shell,并提供 "启动 Cloud Shell " 按钮。Open Cloud Shell in your browser with the Launch Cloud Shell button.
    • 选择 Azure 门户右上角菜单上的“Cloud Shell”Cloud Shell按钮。Select the Cloud Shell button on the menu in the upper-right corner of the Azure portal.

    有关详细信息,请参阅 Azure Cloud Shell Azure CLI概述For more information, see Azure CLI and Overview of Azure Cloud Shell.

  2. 如果尚未通过身份验证,请在 az login 命令中登录。If you aren't already authenticated, sign in with the az login command.

  3. 使用以下命令创建资源组,其中 {RESOURCE GROUP NAME} 是新资源组的资源组名称,{LOCATION} 是 Azure 区域(datacenter):Create a resource group with the following command, where {RESOURCE GROUP NAME} is the resource group name for the new resource group and {LOCATION} is the Azure region (datacenter):

    az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  4. 使用以下命令在资源组中创建密钥保管库,其中 {KEY VAULT NAME} 是新密钥保管库的名称,{LOCATION} 是 Azure 区域(datacenter):Create a key vault in the resource group with the following command, where {KEY VAULT NAME} is the name for the new key vault and {LOCATION} is the Azure region (datacenter):

    az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  5. 在密钥保管库中,以名称-值对的形式创建机密。Create secrets in the key vault as name-value pairs.

    Azure Key Vault 机密名称仅限字母数字字符和短划线。Azure Key Vault secret names are limited to alphanumeric characters and dashes. 分层值(配置节)使用 -- (两个短划线)作为分隔符。Hierarchical values (configuration sections) use -- (two dashes) as a separator. 冒号(通常用于在ASP.NET Core 配置中的子项中分隔部分)在 key vault 机密名称中不允许使用。Colons, which are normally used to delimit a section from a subkey in ASP.NET Core configuration, aren't allowed in key vault secret names. 因此,当机密加载到应用的配置中时,将使用两个短划线并为冒号交换。Therefore, two dashes are used and swapped for a colon when the secrets are loaded into the app's configuration.

    以下机密用于示例应用。The following secrets are for use with the sample app. 这些值包括一个 _prod 后缀,以将其与从用户机密的开发环境中加载的 _dev 后缀值区分开来。The values include a _prod suffix to distinguish them from the _dev suffix values loaded in the Development environment from User Secrets. {KEY VAULT NAME} 替换为在上一步中创建的密钥保管库的名称:Replace {KEY VAULT NAME} with the name of the key vault that you created in the prior step:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
    

为非 Azure 托管的应用使用应用程序 ID 和 x.509 证书Use Application ID and X.509 certificate for non-Azure-hosted apps

配置 Azure AD、Azure Key Vault 和应用,以便在Azure 外托管应用时使用 Azure Active Directory 应用程序 ID 和 x.509 证书对密钥保管库进行身份验证。Configure Azure AD, Azure Key Vault, and the app to use an Azure Active Directory Application ID and X.509 certificate to authenticate to a key vault when the app is hosted outside of Azure. 有关详细信息,请参阅关于密钥、机密和证书For more information, see About keys, secrets, and certificates.

备注

尽管在 Azure 中托管的应用程序支持使用应用程序 ID 和 x.509 证书,但在 Azure 中托管应用程序时,我们建议使用Azure 资源的托管标识Although using an Application ID and X.509 certificate is supported for apps hosted in Azure, we recommend using Managed identities for Azure resources when hosting an app in Azure. 托管标识不需要在应用或开发环境中存储证书。Managed identities don't require storing a certificate in the app or in the development environment.

Program.cs文件顶部的 #define 语句设置为 Certificate时,示例应用使用应用程序 ID 和 x.509 证书。The sample app uses an Application ID and X.509 certificate when the #define statement at the top of the Program.cs file is set to Certificate.

  1. 创建 PKCS # 12 存档( .pfx)证书。Create a PKCS#12 archive (.pfx) certificate. 用于创建证书的选项包括 Windows 和OpenSSL上的 MakeCertOptions for creating certificates include MakeCert on Windows and OpenSSL.
  2. 将证书安装到当前用户的个人证书存储中。Install the certificate into the current user's personal certificate store. 将密钥标记为可导出是可选的。Marking the key as exportable is optional. 记下证书的指纹,此过程将在此过程中使用。Note the certificate's thumbprint, which is used later in this process.
  3. 将 PKCS # 12 存档( .pfx)证书导出为 DER 编码的证书( .cer)。Export the PKCS#12 archive (.pfx) certificate as a DER-encoded certificate (.cer).
  4. 向 Azure AD (应用注册)注册应用。Register the app with Azure AD (App registrations).
  5. 将 DER 编码的证书( .cer)上载到 Azure AD:Upload the DER-encoded certificate (.cer) to Azure AD:
    1. 在 Azure AD 中选择应用。Select the app in Azure AD.
    2. 导航到 "证书" & "机密"。Navigate to Certificates & secrets.
    3. 选择 "上传证书",上传包含公钥的证书。Select Upload certificate to upload the certificate, which contains the public key. .Cerpem.crt证书是可接受的。A .cer, .pem, or .crt certificate is acceptable.
  6. 将密钥保管库名称、应用程序 ID 和证书指纹存储在应用的appsettings文件中。Store the key vault name, Application ID, and certificate thumbprint in the app's appsettings.json file.
  7. 导航到 Azure 门户中的密钥保管库Navigate to Key vaults in the Azure portal.
  8. 选择在生产环境中的机密存储中创建的密钥保管库,其中 Azure Key Vault "部分。Select the key vault that you created in the Secret storage in the Production environment with Azure Key Vault section.
  9. 选择“访问策略”。Select Access policies.
  10. 选择 "添加访问策略"。Select Add Access Policy.
  11. 打开机密权限,并为应用提供GetList权限。Open Secret permissions and provide the app with Get and List permissions.
  12. 选择 "选择主体",并按名称选择注册的应用。Select Select principal and select the registered app by name. 选择“选择”按钮。Select the Select button.
  13. 选择“确定”。Select OK.
  14. 选择“保存”。Select Save.
  15. 部署应用程序。Deploy the app.

Certificate 示例应用从 IConfigurationRoot 中获取其配置值,名称与机密名称相同:The Certificate sample app obtains its configuration values from IConfigurationRoot with the same name as the secret name:

  • 非分层值: SecretName 的值是通过 config["SecretName"]获取的。Non-hierarchical values: The value for SecretName is obtained with config["SecretName"].
  • 分层值(节):使用 : (冒号)表示法或 GetSection 扩展方法。Hierarchical values (sections): Use : (colon) notation or the GetSection extension method. 使用以下任一方法来获取配置值:Use either of these approaches to obtain the configuration value:
    • config["Section:SecretName"]
    • config.GetSection("Section")["SecretName"]

X.509 证书由操作系统管理。The X.509 certificate is managed by the OS. 应用使用appsettings文件提供的值调用 AddAzureKeyVaultThe app calls AddAzureKeyVault with values supplied by the appsettings.json file:

// using System.Linq;
// using System.Security.Cryptography.X509Certificates;
// using Microsoft.Extensions.Configuration;

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            if (context.HostingEnvironment.IsProduction())
            {
                var builtConfig = config.Build();

                using (var store = new X509Store(StoreLocation.CurrentUser))
                {
                    store.Open(OpenFlags.ReadOnly);
                    var certs = store.Certificates
                        .Find(X509FindType.FindByThumbprint,
                            builtConfig["AzureADCertThumbprint"], false);

                    config.AddAzureKeyVault(
                        $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
                        builtConfig["AzureADApplicationId"],
                        certs.OfType<X509Certificate2>().Single());

                    store.Close();
                }
            }
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

示例值:Example values:

  • 密钥保管库名称: contosovaultKey vault name: contosovault
  • 应用程序 ID: 627e911e-43cc-61d4-992e-12db9c81b413Application ID: 627e911e-43cc-61d4-992e-12db9c81b413
  • 证书指纹: fe14593dd66b2406c5269d742d04b6e1ab03adb1Certificate thumbprint: fe14593dd66b2406c5269d742d04b6e1ab03adb1

appsettings.json:appsettings.json:

{
  "KeyVaultName": "Key Vault Name",
  "AzureADApplicationId": "Azure AD Application ID",
  "AzureADCertThumbprint": "Azure AD Certificate Thumbprint"
}

运行应用时,网页将显示加载的机密值。When you run the app, a webpage shows the loaded secret values. 在开发环境中,用 _dev 后缀加载机密值。In the Development environment, secret values load with the _dev suffix. 在生产环境中,值以 _prod 后缀进行加载。In the Production environment, the values load with the _prod suffix.

使用 Azure 资源的托管标识Use Managed identities for Azure resources

部署到 azure 的应用可以利用Azure 资源的托管标识,这允许应用通过使用 Azure AD 身份验证而不使用应用中存储的凭据(应用程序 ID 和密码/客户端密码)进行身份验证 Azure Key Vault。An app deployed to Azure can take advantage of Managed identities for Azure resources, which allows the app to authenticate with Azure Key Vault using Azure AD authentication without credentials (Application ID and Password/Client Secret) stored in the app.

Program.cs文件顶部的 #define 语句设置为 Managed时,示例应用使用 Azure 资源的托管标识。The sample app uses Managed identities for Azure resources when the #define statement at the top of the Program.cs file is set to Managed.

在应用的appsettings文件中输入保管库名称。Enter the vault name into the app's appsettings.json file. 设置为 Managed 版本时,示例应用不需要应用程序 ID 和密码(客户端密码),因此可以忽略这些配置条目。The sample app doesn't require an Application ID and Password (Client Secret) when set to the Managed version, so you can ignore those configuration entries. 应用将部署到 Azure,Azure 将对应用进行身份验证,以便仅使用存储在appsettings文件中的保管库名称访问 Azure Key Vault。The app is deployed to Azure, and Azure authenticates the app to access Azure Key Vault only using the vault name stored in the appsettings.json file.

将示例应用部署到 Azure App Service。Deploy the sample app to Azure App Service.

在创建服务时,会自动向 Azure AD 注册部署到 Azure App Service 的应用。An app deployed to Azure App Service is automatically registered with Azure AD when the service is created. 从部署中获取对象 ID 以在下面的命令中使用。Obtain the Object ID from the deployment for use in the following command. 对象 ID 显示在应用服务的 "标识" 面板上的 "Azure 门户中。The Object ID is shown in the Azure portal on the Identity panel of the App Service.

使用 Azure CLI 和应用程序的对象 ID,为应用程序提供 listget 权限,以访问密钥保管库:Using Azure CLI and the app's Object ID, provide the app with list and get permissions to access the key vault:

az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list

使用 Azure CLI、PowerShell 或 Azure 门户重启应用Restart the app using Azure CLI, PowerShell, or the Azure portal.

示例应用:The sample app:

  • 创建不带连接字符串的 AzureServiceTokenProvider 类的实例。Creates an instance of the AzureServiceTokenProvider class without a connection string. 如果未提供连接字符串,提供程序将尝试从 Azure 资源的托管标识获取访问令牌。When a connection string isn't provided, the provider attempts to obtain an access token from Managed identities for Azure resources.
  • 使用 AzureServiceTokenProvider 实例令牌回调创建新 KeyVaultClientA new KeyVaultClient is created with the AzureServiceTokenProvider instance token callback.
  • KeyVaultClient 实例与加载所有机密值的 IKeyVaultSecretManager 的默认实现一起使用,并将键名称中的双破折号(--)替换为冒号(:)。The KeyVaultClient instance is used with a default implementation of IKeyVaultSecretManager that loads all secret values and replaces double-dashes (--) with colons (:) in key names.
// using Microsoft.Azure.KeyVault;
// using Microsoft.Azure.Services.AppAuthentication;
// using Microsoft.Extensions.Configuration.AzureKeyVault;

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            if (context.HostingEnvironment.IsProduction())
            {
                var builtConfig = config.Build();

                var azureServiceTokenProvider = new AzureServiceTokenProvider();
                var keyVaultClient = new KeyVaultClient(
                    new KeyVaultClient.AuthenticationCallback(
                        azureServiceTokenProvider.KeyVaultTokenCallback));

                config.AddAzureKeyVault(
                    $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
                    keyVaultClient,
                    new DefaultKeyVaultSecretManager());
            }
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Key vault 名称示例值: contosovaultKey vault name example value: contosovault

appsettings.json:appsettings.json:

{
  "KeyVaultName": "Key Vault Name"
}

运行应用时,网页将显示加载的机密值。When you run the app, a webpage shows the loaded secret values. 在开发环境中,机密值具有 _dev 后缀,因为它们是由用户机密提供的。In the Development environment, secret values have the _dev suffix because they're provided by User Secrets. 在生产环境中,值使用 _prod 后缀加载,因为它们由 Azure Key Vault 提供。In the Production environment, the values load with the _prod suffix because they're provided by Azure Key Vault.

如果收到 Access denied 错误,请确认已向应用程序注册了 Azure AD 并提供了对密钥保管库的访问权限。If you receive an Access denied error, confirm that the app is registered with Azure AD and provided access to the key vault. 确认已在 Azure 中重新启动该服务。Confirm that you've restarted the service in Azure.

有关将提供程序与托管标识和 Azure DevOps 管道一起使用的信息,请参阅使用托管服务标识创建与 VM 的 Azure 资源管理器服务连接For information on using the provider with a managed identity and an Azure DevOps pipeline, see Create an Azure Resource Manager service connection to a VM with a managed service identity.

配置选项Configuration options

AddAzureKeyVault 可以接受 AzureKeyVaultConfigurationOptionsAddAzureKeyVault can accept an AzureKeyVaultConfigurationOptions:

config.AddAzureKeyVault(
    new AzureKeyVaultConfigurationOptions()
    {
        ...
    });
属性Property 说明Description
Client 用于检索值的 KeyVaultClientKeyVaultClient to use for retrieving values.
Manager 用于控制机密加载的 IKeyVaultSecretManager 实例。IKeyVaultSecretManager instance used to control secret loading.
ReloadInterval Timespan 在轮询密钥保管库进行更改时等待。Timespan to wait between attempts at polling the key vault for changes. 默认值为 null (配置不会重新加载)。The default value is null (configuration isn't reloaded).
Vault 密钥保管库 URI。Key vault URI.

使用密钥名称前缀Use a key name prefix

AddAzureKeyVault 提供一个重载,该重载接受 IKeyVaultSecretManager的实现,这允许你控制密钥保管库机密如何转换为配置密钥。AddAzureKeyVault provides an overload that accepts an implementation of IKeyVaultSecretManager, which allows you to control how key vault secrets are converted into configuration keys. 例如,你可以实现接口,以便基于在应用启动时提供的前缀值加载机密值。For example, you can implement the interface to load secret values based on a prefix value you provide at app startup. 例如,你可以根据应用程序的版本加载机密。This allows you, for example, to load secrets based on the version of the app.

警告

不要对 key vault 机密使用前缀,将多个应用的机密放入同一密钥保管库,或将环境机密(例如,开发生产机密)放入同一保管库中。Don't use prefixes on key vault secrets to place secrets for multiple apps into the same key vault or to place environmental secrets (for example, development versus production secrets) into the same vault. 建议不同的应用和开发/生产环境使用不同的密钥保管库,以便隔离应用环境以获得最高级别的安全性。We recommend that different apps and development/production environments use separate key vaults to isolate app environments for the highest level of security.

在下面的示例中,在密钥保管库中建立了机密(并使用开发环境的机密管理器工具)进行 5000-AppSecret (密钥保管库机密名称中不允许使用句点)。In the following example, a secret is established in the key vault (and using the Secret Manager tool for the Development environment) for 5000-AppSecret (periods aren't allowed in key vault secret names). 此机密代表应用程序版本5.0.0.0 的应用程序机密。This secret represents an app secret for version 5.0.0.0 of the app. 对于其他版本的应用,5.1.0.0,会将机密添加到密钥保管库(并使用机密管理器工具)进行 5100-AppSecretFor another version of the app, 5.1.0.0, a secret is added to the key vault (and using the Secret Manager tool) for 5100-AppSecret. 每个应用版本会将其版本控制的机密值作为 AppSecret加载到其配置中,并在加载机密时将版本去除。Each app version loads its versioned secret value into its configuration as AppSecret, stripping off the version as it loads the secret.

使用自定义 IKeyVaultSecretManager调用 AddAzureKeyVaultAddAzureKeyVault is called with a custom IKeyVaultSecretManager:

config.AddAzureKeyVault(
    $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
    builtConfig["AzureADApplicationId"],
    certs.OfType<X509Certificate2>().Single(),
    new PrefixKeyVaultSecretManager(versionPrefix));

IKeyVaultSecretManager 实现将对机密的版本前缀做出反应,以将适当的机密加载到配置中:The IKeyVaultSecretManager implementation reacts to the version prefixes of secrets to load the proper secret into configuration:

  • Load 在其名称以前缀开头时加载密码。Load loads a secret when its name starts with the prefix. 未加载其他机密。Other secrets aren't loaded.
  • GetKey设置用户帐户 :GetKey:
    • 从机密名称中删除前缀。Removes the prefix from the secret name.
    • KeyDelimiter替换任意名称中的两个短划线,这是在配置中使用的分隔符(通常为冒号)。Replaces two dashes in any name with the KeyDelimiter, which is the delimiter used in configuration (usually a colon). Azure Key Vault 不允许在机密名称中使用冒号。Azure Key Vault doesn't allow a colon in secret names.
public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager
{
    private readonly string _prefix;

    public PrefixKeyVaultSecretManager(string prefix)
    {
        _prefix = $"{prefix}-";
    }

    public bool Load(SecretItem secret)
    {
        return secret.Identifier.Name.StartsWith(_prefix);
    }

    public string GetKey(SecretBundle secret)
    {
        return secret.SecretIdentifier.Name
            .Substring(_prefix.Length)
            .Replace("--", ConfigurationPath.KeyDelimiter);
    }
}

Load 方法由提供程序算法调用,该算法会循环访问保管库机密,查找具有版本前缀的文件。The Load method is called by a provider algorithm that iterates through the vault secrets to find the ones that have the version prefix. Load找到版本前缀后,该算法使用 GetKey 方法返回密钥名称的配置名称。When a version prefix is found with Load, the algorithm uses the GetKey method to return the configuration name of the secret name. 它从机密名称中去除版本前缀,并返回密钥名称的其余部分,以便加载到应用的配置名称-值对中。It strips off the version prefix from the secret's name and returns the rest of the secret name for loading into the app's configuration name-value pairs.

实现此方法时:When this approach is implemented:

  1. 应用的项目文件中指定的应用版本。The app's version specified in the app's project file. 在以下示例中,应用的版本设置为 5.0.0.0In the following example, the app's version is set to 5.0.0.0:

    <PropertyGroup>
      <Version>5.0.0.0</Version>
    </PropertyGroup>
    
  2. 确认应用的项目文件中存在 <UserSecretsId> 属性,其中 {GUID} 是用户提供的 GUID:Confirm that a <UserSecretsId> property is present in the app's project file, where {GUID} is a user-supplied GUID:

    <PropertyGroup>
      <UserSecretsId>{GUID}</UserSecretsId>
    </PropertyGroup>
    

    机密管理器工具本地保存以下机密:Save the following secrets locally with the Secret Manager tool:

    dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev"
    dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"
    
  3. 使用以下 Azure CLI 命令在 Azure Key Vault 中保存机密:Secrets are saved in Azure Key Vault using the following Azure CLI commands:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
    
  4. 当应用运行时,将加载密钥保管库机密。When the app is run, the key vault secrets are loaded. 5000-AppSecret 的字符串机密与应用程序的项目文件(5.0.0.0)中指定的应用程序版本相匹配。The string secret for 5000-AppSecret is matched to the app's version specified in the app's project file (5.0.0.0).

  5. 将从键名称中去除版本 5000 (带有短划线)。The version, 5000 (with the dash), is stripped from the key name. 在整个应用程序中,读取带有密钥的配置 AppSecret 加载机密值。Throughout the app, reading configuration with the key AppSecret loads the secret value.

  6. 如果在项目文件中将应用程序的版本更改为 5.1.0.0 并再次运行该应用程序,则返回的机密值将 5.1.0.0_secret_value_dev 在开发环境中,并 5.1.0.0_secret_value_prod 生产环境中。If the app's version is changed in the project file to 5.1.0.0 and the app is run again, the secret value returned is 5.1.0.0_secret_value_dev in the Development environment and 5.1.0.0_secret_value_prod in Production.

备注

你还可以提供自己的 KeyVaultClient 实现以 AddAzureKeyVaultYou can also provide your own KeyVaultClient implementation to AddAzureKeyVault. 自定义客户端允许跨应用共享客户端的单个实例。A custom client permits sharing a single instance of the client across the app.

将数组绑定至类Bind an array to a class

提供程序能够将配置值读入数组,以便绑定到 POCO 数组。The provider is capable of reading configuration values into an array for binding to a POCO array.

从允许键包含冒号(:)分隔符的配置源中进行读取时,将使用数字键段来区分组成数组的键(:0::1:When reading from a configuration source that allows keys to contain colon (:) separators, a numeric key segment is used to distinguish the keys that make up an array (:0:, :1:, … :{n}:)。:{n}:). 有关详细信息,请参阅配置:将数组绑定到类For more information, see Configuration: Bind an array to a class.

Azure Key Vault 密钥不能使用冒号作为分隔符。Azure Key Vault keys can't use a colon as a separator. 本主题中所述的方法使用双短划线(--)作为层次结构值(节)的分隔符。The approach described in this topic uses double dashes (--) as a separator for hierarchical values (sections). 数组键存储在具有双短划线和数值段(--0----1----{n}--) Azure Key Vault 中。Array keys are stored in Azure Key Vault with double dashes and numeric key segments (--0--, --1--, … --{n}--).

检查 JSON 文件提供的以下Serilog日志记录提供程序配置。Examine the following Serilog logging provider configuration provided by a JSON file. WriteTo 数组中定义了两个对象文本,该对象反映了两个 Serilog接收器,它们描述了日志记录输出的目标:There are two object literals defined in the WriteTo array that reflect two Serilog sinks, which describe destinations for logging output:

"Serilog": {
  "WriteTo": [
    {
      "Name": "AzureTableStorage",
      "Args": {
        "storageTableName": "logs",
        "connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
      }
    },
    {
      "Name": "AzureDocumentDB",
      "Args": {
        "endpointUrl": "https://contoso.documents.azure.com:443",
        "authorizationKey": "Eby8...GMGw=="
      }
    }
  ]
}

使用双短划线(--)表示法和数值段 Azure Key Vault 前面的 JSON 文件中所示的配置:The configuration shown in the preceding JSON file is stored in Azure Key Vault using double dash (--) notation and numeric segments:

KeyKey Value
Serilog--WriteTo--0--Name AzureTableStorage
Serilog--WriteTo--0--Args--storageTableName logs
Serilog--WriteTo--0--Args--connectionString DefaultEnd...ountKey=Eby8...GMGw==
Serilog--WriteTo--1--Name AzureDocumentDB
Serilog--WriteTo--1--Args--endpointUrl https://contoso.documents.azure.com:443
Serilog--WriteTo--1--Args--authorizationKey Eby8...GMGw==

重载机密Reload secrets

在调用 IConfigurationRoot.Reload() 之前会缓存机密。Secrets are cached until IConfigurationRoot.Reload() is called. 在执行 Reload 之前,应用程序不会考虑到密钥保管库中已过期、已禁用和更新的机密。Expired, disabled, and updated secrets in the key vault are not respected by the app until Reload is executed.

Configuration.Reload();

禁用和过期的机密Disabled and expired secrets

禁用和过期的机密会引发 KeyVaultErrorExceptionDisabled and expired secrets throw a KeyVaultErrorException. 若要防止应用程序引发,请使用不同的配置提供程序提供配置,或更新禁用或过期的机密。To prevent the app from throwing, provide the configuration using a different configuration provider or update the disabled or expired secret.

故障排除Troubleshoot

当应用无法使用访问接口加载配置时,会将错误消息写入ASP.NET Core 日志记录基础结构When the app fails to load configuration using the provider, an error message is written to the ASP.NET Core Logging infrastructure. 以下条件将阻止加载配置:The following conditions will prevent configuration from loading:

  • Azure Active Directory 中未正确配置应用或证书。The app or certificate isn't configured correctly in Azure Active Directory.
  • Azure Key Vault 中不存在密钥保管库。The key vault doesn't exist in Azure Key Vault.
  • 应用无权访问密钥保管库。The app isn't authorized to access the key vault.
  • 访问策略不包括 GetList 权限。The access policy doesn't include Get and List permissions.
  • 在密钥保管库中,配置数据(名称-值对)被错误地命名、缺失、禁用或过期。In the key vault, the configuration data (name-value pair) is incorrectly named, missing, disabled, or expired.
  • 应用具有错误的密钥保管库名称(KeyVaultName)、Azure AD 应用程序 Id (AzureADApplicationId)或 Azure AD 证书指纹(AzureADCertThumbprint)。The app has the wrong key vault name (KeyVaultName), Azure AD Application Id (AzureADApplicationId), or Azure AD certificate thumbprint (AzureADCertThumbprint).
  • 在应用程序中,配置项(名称)的值不正确。The configuration key (name) is incorrect in the app for the value you're trying to load.
  • 向密钥保管库添加应用的访问策略时,策略已创建,但未在访问策略UI 中选择 "保存" 按钮。When adding the access policy for the app to the key vault, the policy was created, but the Save button wasn't selected in the Access policies UI.

其他资源Additional resources

本文档说明如何使用Microsoft Azure Key Vault配置提供程序从 Azure Key Vault 机密加载应用配置值。This document explains how to use the Microsoft Azure Key Vault Configuration Provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault 是一项基于云的服务,可帮助保护应用程序和服务使用的加密密钥和机密。Azure Key Vault is a cloud-based service that assists in safeguarding cryptographic keys and secrets used by apps and services. 将 Azure Key Vault 用于 ASP.NET Core 应用的常见方案包括:Common scenarios for using Azure Key Vault with ASP.NET Core apps include:

  • 控制对敏感配置数据的访问。Controlling access to sensitive configuration data.
  • 在存储配置数据时满足 FIPS 140-2 级别2验证的硬件安全模块(HSM)的要求。Meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSM's) when storing configuration data.

查看或下载示例代码如何下载View or download sample code (how to download)

Packages

将包引用添加到AzureKeyVault包。Add a package reference to the Microsoft.Extensions.Configuration.AzureKeyVault package.

示例应用Sample app

该示例应用在Program.cs文件顶部的 #define 语句确定的两种模式中运行:The sample app runs in either of two modes determined by the #define statement at the top of the Program.cs file:

  • Certificate – 演示了如何使用 Azure Key Vault 的客户端 ID 和 x.509 证书来访问存储在 Azure Key Vault 中的机密。Certificate – Demonstrates the use of an Azure Key Vault Client ID and X.509 certificate to access secrets stored in Azure Key Vault. 可以从部署到 Azure App Service 的任何位置运行此版本的示例,也可以从部署到 ASP.NET Core 应用程序的任何主机上运行。This version of the sample can be run from any location, deployed to Azure App Service or any host capable of serving an ASP.NET Core app.
  • Managed – 演示了如何使用Azure 资源的托管标识对应用进行身份验证,以使用 Azure AD 身份验证 Azure Key Vault,而不会在应用的代码或配置中存储凭据。Managed – Demonstrates how to use Managed identities for Azure resources to authenticate the app to Azure Key Vault with Azure AD authentication without credentials stored in the app's code or configuration. 使用托管标识进行身份验证时,不需要 Azure AD 的应用程序 ID 和密码(客户端机密)。When using managed identities to authenticate, an Azure AD Application ID and Password (Client Secret) aren't required. 必须将示例的 Managed 版本部署到 Azure。The Managed version of the sample must be deployed to Azure. 按照使用 Azure 资源的托管标识部分中的指导进行操作。Follow the guidance in the Use the Managed identities for Azure resources section.

有关如何使用预处理器指令(#define)配置示例应用的详细信息,请参阅 ASP.NET Core 简介For more information on how to configure a sample app using preprocessor directives (#define), see ASP.NET Core 简介.

开发环境中的机密存储Secret storage in the Development environment

使用机密管理器工具在本地设置机密。Set secrets locally using the Secret Manager tool. 当在开发环境中的本地计算机上运行示例应用时,会从本地密钥管理器存储中加载密码。When the sample app runs on the local machine in the Development environment, secrets are loaded from the local Secret Manager store.

机密管理器工具需要应用的项目文件中的 <UserSecretsId> 属性。The Secret Manager tool requires a <UserSecretsId> property in the app's project file. 将属性值({GUID})设置为任何唯一 GUID:Set the property value ({GUID}) to any unique GUID:

<PropertyGroup>
  <UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>

机密以名称-值对的形式创建。Secrets are created as name-value pairs. 分层值(配置节)使用 : (冒号)作为ASP.NET Core 配置项名称中的分隔符。Hierarchical values (configuration sections) use a : (colon) as a separator in ASP.NET Core configuration key names.

机密管理器是从打开到项目内容根目录的命令 shell 使用的,其中 {SECRET NAME} 是名称,{SECRET VALUE} 是值:The Secret Manager is used from a command shell opened to the project's content root, where {SECRET NAME} is the name and {SECRET VALUE} is the value:

dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"

从项目的内容根在命令外壳中执行以下命令,为示例应用设置机密:Execute the following commands in a command shell from the project's content root to set the secrets for the sample app:

dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"

如果这些机密存储在生产环境中的机密存储 Azure Key Vault 中,并具有 Azure Key Vault部分,则 _dev 后缀将更改为 _prodWhen these secrets are stored in Azure Key Vault in the Secret storage in the Production environment with Azure Key Vault section, the _dev suffix is changed to _prod. 后缀在应用的输出中提供视觉提示,指示配置值的源。The suffix provides a visual cue in the app's output indicating the source of the configuration values.

Azure Key Vault 中的生产环境中的机密存储Secret storage in the Production environment with Azure Key Vault

快速入门:使用 Azure CLI 主题的 Azure Key Vault 设置和检索机密的说明,请参阅此处,以创建 Azure Key Vault 和存储示例应用使用的机密。The instructions provided by the Quickstart: Set and retrieve a secret from Azure Key Vault using Azure CLI topic are summarized here for creating an Azure Key Vault and storing secrets used by the sample app. 有关更多详细信息,请参阅主题。Refer to the topic for further details.

  1. 使用Azure 门户中的以下方法之一打开 Azure Cloud shell:Open Azure Cloud shell using any one of the following methods in the Azure portal:

    • 选择代码块右上角的“试用”。Select Try It in the upper-right corner of a code block. 在文本框中使用搜索字符串 "Azure CLI"。Use the search string "Azure CLI" in the text box.
    • 在浏览器中打开 Cloud Shell,并提供 "启动 Cloud Shell " 按钮。Open Cloud Shell in your browser with the Launch Cloud Shell button.
    • 选择 Azure 门户右上角菜单上的“Cloud Shell”Cloud Shell按钮。Select the Cloud Shell button on the menu in the upper-right corner of the Azure portal.

    有关详细信息,请参阅 Azure Cloud Shell Azure CLI概述For more information, see Azure CLI and Overview of Azure Cloud Shell.

  2. 如果尚未通过身份验证,请在 az login 命令中登录。If you aren't already authenticated, sign in with the az login command.

  3. 使用以下命令创建资源组,其中 {RESOURCE GROUP NAME} 是新资源组的资源组名称,{LOCATION} 是 Azure 区域(datacenter):Create a resource group with the following command, where {RESOURCE GROUP NAME} is the resource group name for the new resource group and {LOCATION} is the Azure region (datacenter):

    az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  4. 使用以下命令在资源组中创建密钥保管库,其中 {KEY VAULT NAME} 是新密钥保管库的名称,{LOCATION} 是 Azure 区域(datacenter):Create a key vault in the resource group with the following command, where {KEY VAULT NAME} is the name for the new key vault and {LOCATION} is the Azure region (datacenter):

    az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  5. 在密钥保管库中,以名称-值对的形式创建机密。Create secrets in the key vault as name-value pairs.

    Azure Key Vault 机密名称仅限字母数字字符和短划线。Azure Key Vault secret names are limited to alphanumeric characters and dashes. 分层值(配置节)使用 -- (两个短划线)作为分隔符。Hierarchical values (configuration sections) use -- (two dashes) as a separator. 冒号(通常用于在ASP.NET Core 配置中的子项中分隔部分)在 key vault 机密名称中不允许使用。Colons, which are normally used to delimit a section from a subkey in ASP.NET Core configuration, aren't allowed in key vault secret names. 因此,当机密加载到应用的配置中时,将使用两个短划线并为冒号交换。Therefore, two dashes are used and swapped for a colon when the secrets are loaded into the app's configuration.

    以下机密用于示例应用。The following secrets are for use with the sample app. 这些值包括一个 _prod 后缀,以将其与从用户机密的开发环境中加载的 _dev 后缀值区分开来。The values include a _prod suffix to distinguish them from the _dev suffix values loaded in the Development environment from User Secrets. {KEY VAULT NAME} 替换为在上一步中创建的密钥保管库的名称:Replace {KEY VAULT NAME} with the name of the key vault that you created in the prior step:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
    

为非 Azure 托管的应用使用应用程序 ID 和 x.509 证书Use Application ID and X.509 certificate for non-Azure-hosted apps

配置 Azure AD、Azure Key Vault 和应用,以便在Azure 外托管应用时使用 Azure Active Directory 应用程序 ID 和 x.509 证书对密钥保管库进行身份验证。Configure Azure AD, Azure Key Vault, and the app to use an Azure Active Directory Application ID and X.509 certificate to authenticate to a key vault when the app is hosted outside of Azure. 有关详细信息,请参阅关于密钥、机密和证书For more information, see About keys, secrets, and certificates.

备注

尽管在 Azure 中托管的应用程序支持使用应用程序 ID 和 x.509 证书,但在 Azure 中托管应用程序时,我们建议使用Azure 资源的托管标识Although using an Application ID and X.509 certificate is supported for apps hosted in Azure, we recommend using Managed identities for Azure resources when hosting an app in Azure. 托管标识不需要在应用或开发环境中存储证书。Managed identities don't require storing a certificate in the app or in the development environment.

Program.cs文件顶部的 #define 语句设置为 Certificate时,示例应用使用应用程序 ID 和 x.509 证书。The sample app uses an Application ID and X.509 certificate when the #define statement at the top of the Program.cs file is set to Certificate.

  1. 创建 PKCS # 12 存档( .pfx)证书。Create a PKCS#12 archive (.pfx) certificate. 用于创建证书的选项包括 Windows 和OpenSSL上的 MakeCertOptions for creating certificates include MakeCert on Windows and OpenSSL.
  2. 将证书安装到当前用户的个人证书存储中。Install the certificate into the current user's personal certificate store. 将密钥标记为可导出是可选的。Marking the key as exportable is optional. 记下证书的指纹,此过程将在此过程中使用。Note the certificate's thumbprint, which is used later in this process.
  3. 将 PKCS # 12 存档( .pfx)证书导出为 DER 编码的证书( .cer)。Export the PKCS#12 archive (.pfx) certificate as a DER-encoded certificate (.cer).
  4. 向 Azure AD (应用注册)注册应用。Register the app with Azure AD (App registrations).
  5. 将 DER 编码的证书( .cer)上载到 Azure AD:Upload the DER-encoded certificate (.cer) to Azure AD:
    1. 在 Azure AD 中选择应用。Select the app in Azure AD.
    2. 导航到 "证书" & "机密"。Navigate to Certificates & secrets.
    3. 选择 "上传证书",上传包含公钥的证书。Select Upload certificate to upload the certificate, which contains the public key. .Cerpem.crt证书是可接受的。A .cer, .pem, or .crt certificate is acceptable.
  6. 将密钥保管库名称、应用程序 ID 和证书指纹存储在应用的appsettings文件中。Store the key vault name, Application ID, and certificate thumbprint in the app's appsettings.json file.
  7. 导航到 Azure 门户中的密钥保管库Navigate to Key vaults in the Azure portal.
  8. 选择在生产环境中的机密存储中创建的密钥保管库,其中 Azure Key Vault "部分。Select the key vault that you created in the Secret storage in the Production environment with Azure Key Vault section.
  9. 选择“访问策略”。Select Access policies.
  10. 选择 "添加访问策略"。Select Add Access Policy.
  11. 打开机密权限,并为应用提供GetList权限。Open Secret permissions and provide the app with Get and List permissions.
  12. 选择 "选择主体",并按名称选择注册的应用。Select Select principal and select the registered app by name. 选择“选择”按钮。Select the Select button.
  13. 选择“确定”。Select OK.
  14. 选择“保存”。Select Save.
  15. 部署应用程序。Deploy the app.

Certificate 示例应用从 IConfigurationRoot 中获取其配置值,名称与机密名称相同:The Certificate sample app obtains its configuration values from IConfigurationRoot with the same name as the secret name:

  • 非分层值: SecretName 的值是通过 config["SecretName"]获取的。Non-hierarchical values: The value for SecretName is obtained with config["SecretName"].
  • 分层值(节):使用 : (冒号)表示法或 GetSection 扩展方法。Hierarchical values (sections): Use : (colon) notation or the GetSection extension method. 使用以下任一方法来获取配置值:Use either of these approaches to obtain the configuration value:
    • config["Section:SecretName"]
    • config.GetSection("Section")["SecretName"]

X.509 证书由操作系统管理。The X.509 certificate is managed by the OS. 应用使用appsettings文件提供的值调用 AddAzureKeyVaultThe app calls AddAzureKeyVault with values supplied by the appsettings.json file:

// using System.Linq;
// using System.Security.Cryptography.X509Certificates;
// using Microsoft.Extensions.Configuration;

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            if (context.HostingEnvironment.IsProduction())
            {
                var builtConfig = config.Build();

                using (var store = new X509Store(StoreLocation.CurrentUser))
                {
                    store.Open(OpenFlags.ReadOnly);
                    var certs = store.Certificates
                        .Find(X509FindType.FindByThumbprint,
                            builtConfig["AzureADCertThumbprint"], false);

                    config.AddAzureKeyVault(
                        $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
                        builtConfig["AzureADApplicationId"],
                        certs.OfType<X509Certificate2>().Single());

                    store.Close();
                }
            }
        })
        .UseStartup<Startup>();

示例值:Example values:

  • 密钥保管库名称: contosovaultKey vault name: contosovault
  • 应用程序 ID: 627e911e-43cc-61d4-992e-12db9c81b413Application ID: 627e911e-43cc-61d4-992e-12db9c81b413
  • 证书指纹: fe14593dd66b2406c5269d742d04b6e1ab03adb1Certificate thumbprint: fe14593dd66b2406c5269d742d04b6e1ab03adb1

appsettings.json:appsettings.json:

{
  "KeyVaultName": "Key Vault Name",
  "AzureADApplicationId": "Azure AD Application ID",
  "AzureADCertThumbprint": "Azure AD Certificate Thumbprint"
}

运行应用时,网页将显示加载的机密值。When you run the app, a webpage shows the loaded secret values. 在开发环境中,用 _dev 后缀加载机密值。In the Development environment, secret values load with the _dev suffix. 在生产环境中,值以 _prod 后缀进行加载。In the Production environment, the values load with the _prod suffix.

使用 Azure 资源的托管标识Use Managed identities for Azure resources

部署到 azure 的应用可以利用Azure 资源的托管标识,这允许应用通过使用 Azure AD 身份验证而不使用应用中存储的凭据(应用程序 ID 和密码/客户端密码)进行身份验证 Azure Key Vault。An app deployed to Azure can take advantage of Managed identities for Azure resources, which allows the app to authenticate with Azure Key Vault using Azure AD authentication without credentials (Application ID and Password/Client Secret) stored in the app.

Program.cs文件顶部的 #define 语句设置为 Managed时,示例应用使用 Azure 资源的托管标识。The sample app uses Managed identities for Azure resources when the #define statement at the top of the Program.cs file is set to Managed.

在应用的appsettings文件中输入保管库名称。Enter the vault name into the app's appsettings.json file. 设置为 Managed 版本时,示例应用不需要应用程序 ID 和密码(客户端密码),因此可以忽略这些配置条目。The sample app doesn't require an Application ID and Password (Client Secret) when set to the Managed version, so you can ignore those configuration entries. 应用将部署到 Azure,Azure 将对应用进行身份验证,以便仅使用存储在appsettings文件中的保管库名称访问 Azure Key Vault。The app is deployed to Azure, and Azure authenticates the app to access Azure Key Vault only using the vault name stored in the appsettings.json file.

将示例应用部署到 Azure App Service。Deploy the sample app to Azure App Service.

在创建服务时,会自动向 Azure AD 注册部署到 Azure App Service 的应用。An app deployed to Azure App Service is automatically registered with Azure AD when the service is created. 从部署中获取对象 ID 以在下面的命令中使用。Obtain the Object ID from the deployment for use in the following command. 对象 ID 显示在应用服务的 "标识" 面板上的 "Azure 门户中。The Object ID is shown in the Azure portal on the Identity panel of the App Service.

使用 Azure CLI 和应用程序的对象 ID,为应用程序提供 listget 权限,以访问密钥保管库:Using Azure CLI and the app's Object ID, provide the app with list and get permissions to access the key vault:

az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list

使用 Azure CLI、PowerShell 或 Azure 门户重启应用Restart the app using Azure CLI, PowerShell, or the Azure portal.

示例应用:The sample app:

  • 创建不带连接字符串的 AzureServiceTokenProvider 类的实例。Creates an instance of the AzureServiceTokenProvider class without a connection string. 如果未提供连接字符串,提供程序将尝试从 Azure 资源的托管标识获取访问令牌。When a connection string isn't provided, the provider attempts to obtain an access token from Managed identities for Azure resources.
  • 使用 AzureServiceTokenProvider 实例令牌回调创建新 KeyVaultClientA new KeyVaultClient is created with the AzureServiceTokenProvider instance token callback.
  • KeyVaultClient 实例与加载所有机密值的 IKeyVaultSecretManager 的默认实现一起使用,并将键名称中的双破折号(--)替换为冒号(:)。The KeyVaultClient instance is used with a default implementation of IKeyVaultSecretManager that loads all secret values and replaces double-dashes (--) with colons (:) in key names.
// using Microsoft.Azure.KeyVault;
// using Microsoft.Azure.Services.AppAuthentication;
// using Microsoft.Extensions.Configuration.AzureKeyVault;

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            if (context.HostingEnvironment.IsProduction())
            {
                var builtConfig = config.Build();

                var azureServiceTokenProvider = new AzureServiceTokenProvider();
                var keyVaultClient = new KeyVaultClient(
                    new KeyVaultClient.AuthenticationCallback(
                        azureServiceTokenProvider.KeyVaultTokenCallback));

                config.AddAzureKeyVault(
                    $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
                    keyVaultClient,
                    new DefaultKeyVaultSecretManager());
            }
        })
        .UseStartup<Startup>();

Key vault 名称示例值: contosovaultKey vault name example value: contosovault

appsettings.json:appsettings.json:

{
  "KeyVaultName": "Key Vault Name"
}

运行应用时,网页将显示加载的机密值。When you run the app, a webpage shows the loaded secret values. 在开发环境中,机密值具有 _dev 后缀,因为它们是由用户机密提供的。In the Development environment, secret values have the _dev suffix because they're provided by User Secrets. 在生产环境中,值使用 _prod 后缀加载,因为它们由 Azure Key Vault 提供。In the Production environment, the values load with the _prod suffix because they're provided by Azure Key Vault.

如果收到 Access denied 错误,请确认已向应用程序注册了 Azure AD 并提供了对密钥保管库的访问权限。If you receive an Access denied error, confirm that the app is registered with Azure AD and provided access to the key vault. 确认已在 Azure 中重新启动该服务。Confirm that you've restarted the service in Azure.

有关将提供程序与托管标识和 Azure DevOps 管道一起使用的信息,请参阅使用托管服务标识创建与 VM 的 Azure 资源管理器服务连接For information on using the provider with a managed identity and an Azure DevOps pipeline, see Create an Azure Resource Manager service connection to a VM with a managed service identity.

使用密钥名称前缀Use a key name prefix

AddAzureKeyVault 提供一个重载,该重载接受 IKeyVaultSecretManager的实现,这允许你控制密钥保管库机密如何转换为配置密钥。AddAzureKeyVault provides an overload that accepts an implementation of IKeyVaultSecretManager, which allows you to control how key vault secrets are converted into configuration keys. 例如,你可以实现接口,以便基于在应用启动时提供的前缀值加载机密值。For example, you can implement the interface to load secret values based on a prefix value you provide at app startup. 例如,你可以根据应用程序的版本加载机密。This allows you, for example, to load secrets based on the version of the app.

警告

不要对 key vault 机密使用前缀,将多个应用的机密放入同一密钥保管库,或将环境机密(例如,开发生产机密)放入同一保管库中。Don't use prefixes on key vault secrets to place secrets for multiple apps into the same key vault or to place environmental secrets (for example, development versus production secrets) into the same vault. 建议不同的应用和开发/生产环境使用不同的密钥保管库,以便隔离应用环境以获得最高级别的安全性。We recommend that different apps and development/production environments use separate key vaults to isolate app environments for the highest level of security.

在下面的示例中,在密钥保管库中建立了机密(并使用开发环境的机密管理器工具)进行 5000-AppSecret (密钥保管库机密名称中不允许使用句点)。In the following example, a secret is established in the key vault (and using the Secret Manager tool for the Development environment) for 5000-AppSecret (periods aren't allowed in key vault secret names). 此机密代表应用程序版本5.0.0.0 的应用程序机密。This secret represents an app secret for version 5.0.0.0 of the app. 对于其他版本的应用,5.1.0.0,会将机密添加到密钥保管库(并使用机密管理器工具)进行 5100-AppSecretFor another version of the app, 5.1.0.0, a secret is added to the key vault (and using the Secret Manager tool) for 5100-AppSecret. 每个应用版本会将其版本控制的机密值作为 AppSecret加载到其配置中,并在加载机密时将版本去除。Each app version loads its versioned secret value into its configuration as AppSecret, stripping off the version as it loads the secret.

使用自定义 IKeyVaultSecretManager调用 AddAzureKeyVaultAddAzureKeyVault is called with a custom IKeyVaultSecretManager:

config.AddAzureKeyVault(
    $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
    builtConfig["AzureADApplicationId"],
    certs.OfType<X509Certificate2>().Single(),
    new PrefixKeyVaultSecretManager(versionPrefix));

IKeyVaultSecretManager 实现将对机密的版本前缀做出反应,以将适当的机密加载到配置中:The IKeyVaultSecretManager implementation reacts to the version prefixes of secrets to load the proper secret into configuration:

  • Load 在其名称以前缀开头时加载密码。Load loads a secret when its name starts with the prefix. 未加载其他机密。Other secrets aren't loaded.
  • GetKey设置用户帐户 :GetKey:
    • 从机密名称中删除前缀。Removes the prefix from the secret name.
    • KeyDelimiter替换任意名称中的两个短划线,这是在配置中使用的分隔符(通常为冒号)。Replaces two dashes in any name with the KeyDelimiter, which is the delimiter used in configuration (usually a colon). Azure Key Vault 不允许在机密名称中使用冒号。Azure Key Vault doesn't allow a colon in secret names.
public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager
{
    private readonly string _prefix;

    public PrefixKeyVaultSecretManager(string prefix)
    {
        _prefix = $"{prefix}-";
    }

    public bool Load(SecretItem secret)
    {
        return secret.Identifier.Name.StartsWith(_prefix);
    }

    public string GetKey(SecretBundle secret)
    {
        return secret.SecretIdentifier.Name
            .Substring(_prefix.Length)
            .Replace("--", ConfigurationPath.KeyDelimiter);
    }
}

Load 方法由提供程序算法调用,该算法会循环访问保管库机密,查找具有版本前缀的文件。The Load method is called by a provider algorithm that iterates through the vault secrets to find the ones that have the version prefix. Load找到版本前缀后,该算法使用 GetKey 方法返回密钥名称的配置名称。When a version prefix is found with Load, the algorithm uses the GetKey method to return the configuration name of the secret name. 它从机密名称中去除版本前缀,并返回密钥名称的其余部分,以便加载到应用的配置名称-值对中。It strips off the version prefix from the secret's name and returns the rest of the secret name for loading into the app's configuration name-value pairs.

实现此方法时:When this approach is implemented:

  1. 应用的项目文件中指定的应用版本。The app's version specified in the app's project file. 在以下示例中,应用的版本设置为 5.0.0.0In the following example, the app's version is set to 5.0.0.0:

    <PropertyGroup>
      <Version>5.0.0.0</Version>
    </PropertyGroup>
    
  2. 确认应用的项目文件中存在 <UserSecretsId> 属性,其中 {GUID} 是用户提供的 GUID:Confirm that a <UserSecretsId> property is present in the app's project file, where {GUID} is a user-supplied GUID:

    <PropertyGroup>
      <UserSecretsId>{GUID}</UserSecretsId>
    </PropertyGroup>
    

    机密管理器工具本地保存以下机密:Save the following secrets locally with the Secret Manager tool:

    dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev"
    dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"
    
  3. 使用以下 Azure CLI 命令在 Azure Key Vault 中保存机密:Secrets are saved in Azure Key Vault using the following Azure CLI commands:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
    
  4. 当应用运行时,将加载密钥保管库机密。When the app is run, the key vault secrets are loaded. 5000-AppSecret 的字符串机密与应用程序的项目文件(5.0.0.0)中指定的应用程序版本相匹配。The string secret for 5000-AppSecret is matched to the app's version specified in the app's project file (5.0.0.0).

  5. 将从键名称中去除版本 5000 (带有短划线)。The version, 5000 (with the dash), is stripped from the key name. 在整个应用程序中,读取带有密钥的配置 AppSecret 加载机密值。Throughout the app, reading configuration with the key AppSecret loads the secret value.

  6. 如果在项目文件中将应用程序的版本更改为 5.1.0.0 并再次运行该应用程序,则返回的机密值将 5.1.0.0_secret_value_dev 在开发环境中,并 5.1.0.0_secret_value_prod 生产环境中。If the app's version is changed in the project file to 5.1.0.0 and the app is run again, the secret value returned is 5.1.0.0_secret_value_dev in the Development environment and 5.1.0.0_secret_value_prod in Production.

备注

你还可以提供自己的 KeyVaultClient 实现以 AddAzureKeyVaultYou can also provide your own KeyVaultClient implementation to AddAzureKeyVault. 自定义客户端允许跨应用共享客户端的单个实例。A custom client permits sharing a single instance of the client across the app.

将数组绑定至类Bind an array to a class

提供程序能够将配置值读入数组,以便绑定到 POCO 数组。The provider is capable of reading configuration values into an array for binding to a POCO array.

从允许键包含冒号(:)分隔符的配置源中进行读取时,将使用数字键段来区分组成数组的键(:0::1:When reading from a configuration source that allows keys to contain colon (:) separators, a numeric key segment is used to distinguish the keys that make up an array (:0:, :1:, … :{n}:)。:{n}:). 有关详细信息,请参阅配置:将数组绑定到类For more information, see Configuration: Bind an array to a class.

Azure Key Vault 密钥不能使用冒号作为分隔符。Azure Key Vault keys can't use a colon as a separator. 本主题中所述的方法使用双短划线(--)作为层次结构值(节)的分隔符。The approach described in this topic uses double dashes (--) as a separator for hierarchical values (sections). 数组键存储在具有双短划线和数值段(--0----1----{n}--) Azure Key Vault 中。Array keys are stored in Azure Key Vault with double dashes and numeric key segments (--0--, --1--, … --{n}--).

检查 JSON 文件提供的以下Serilog日志记录提供程序配置。Examine the following Serilog logging provider configuration provided by a JSON file. WriteTo 数组中定义了两个对象文本,该对象反映了两个 Serilog接收器,它们描述了日志记录输出的目标:There are two object literals defined in the WriteTo array that reflect two Serilog sinks, which describe destinations for logging output:

"Serilog": {
  "WriteTo": [
    {
      "Name": "AzureTableStorage",
      "Args": {
        "storageTableName": "logs",
        "connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
      }
    },
    {
      "Name": "AzureDocumentDB",
      "Args": {
        "endpointUrl": "https://contoso.documents.azure.com:443",
        "authorizationKey": "Eby8...GMGw=="
      }
    }
  ]
}

使用双短划线(--)表示法和数值段 Azure Key Vault 前面的 JSON 文件中所示的配置:The configuration shown in the preceding JSON file is stored in Azure Key Vault using double dash (--) notation and numeric segments:

KeyKey Value
Serilog--WriteTo--0--Name AzureTableStorage
Serilog--WriteTo--0--Args--storageTableName logs
Serilog--WriteTo--0--Args--connectionString DefaultEnd...ountKey=Eby8...GMGw==
Serilog--WriteTo--1--Name AzureDocumentDB
Serilog--WriteTo--1--Args--endpointUrl https://contoso.documents.azure.com:443
Serilog--WriteTo--1--Args--authorizationKey Eby8...GMGw==

重载机密Reload secrets

在调用 IConfigurationRoot.Reload() 之前会缓存机密。Secrets are cached until IConfigurationRoot.Reload() is called. 在执行 Reload 之前,应用程序不会考虑到密钥保管库中已过期、已禁用和更新的机密。Expired, disabled, and updated secrets in the key vault are not respected by the app until Reload is executed.

Configuration.Reload();

禁用和过期的机密Disabled and expired secrets

禁用和过期的机密会引发 KeyVaultErrorExceptionDisabled and expired secrets throw a KeyVaultErrorException. 若要防止应用程序引发,请使用不同的配置提供程序提供配置,或更新禁用或过期的机密。To prevent the app from throwing, provide the configuration using a different configuration provider or update the disabled or expired secret.

故障排除Troubleshoot

当应用无法使用访问接口加载配置时,会将错误消息写入ASP.NET Core 日志记录基础结构When the app fails to load configuration using the provider, an error message is written to the ASP.NET Core Logging infrastructure. 以下条件将阻止加载配置:The following conditions will prevent configuration from loading:

  • Azure Active Directory 中未正确配置应用或证书。The app or certificate isn't configured correctly in Azure Active Directory.
  • Azure Key Vault 中不存在密钥保管库。The key vault doesn't exist in Azure Key Vault.
  • 应用无权访问密钥保管库。The app isn't authorized to access the key vault.
  • 访问策略不包括 GetList 权限。The access policy doesn't include Get and List permissions.
  • 在密钥保管库中,配置数据(名称-值对)被错误地命名、缺失、禁用或过期。In the key vault, the configuration data (name-value pair) is incorrectly named, missing, disabled, or expired.
  • 应用具有错误的密钥保管库名称(KeyVaultName)、Azure AD 应用程序 Id (AzureADApplicationId)或 Azure AD 证书指纹(AzureADCertThumbprint)。The app has the wrong key vault name (KeyVaultName), Azure AD Application Id (AzureADApplicationId), or Azure AD certificate thumbprint (AzureADCertThumbprint).
  • 在应用程序中,配置项(名称)的值不正确。The configuration key (name) is incorrect in the app for the value you're trying to load.
  • 向密钥保管库添加应用的访问策略时,策略已创建,但未在访问策略UI 中选择 "保存" 按钮。When adding the access policy for the app to the key vault, the policy was created, but the Save button wasn't selected in the Access policies UI.

其他资源Additional resources