.NET 7 Web API指纹认证加载Azure Key Vault密钥失败
.NET 7 Web API通过指纹认证从Azure Key Vault加载密钥失败问题
我在.NET 7 Web API应用中尝试通过指纹认证从Azure Key Vault加载密钥值,已实现相关配置代码,但调试时ServiceBusConfigOption.ConnectionString的值仍显示为@Microsoft.KeyVault(SecretUri={SecretURL}/),未加载实际密钥值。相关代码如下:
配置扩展方法
public static class ConfigurationExtensions { public static IConfigurationBuilder AddAppConfiguration(this IConfigurationBuilder configurationBuilder, IWebHostEnvironment hostingEnvironment) { configurationBuilder .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); // For Localhost, we're using appsettings.json file, it will not be overridden when running on Localhost if (!hostingEnvironment.IsEnvironment("Localhost")) { configurationBuilder.AddJsonFile($"appsettings.{hostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true); } configurationBuilder.AddEnvironmentVariables(); return configurationBuilder; } public static IConfigurationBuilder AddAzureKeyVaultConfiguration(this IConfigurationBuilder configurationBuilder, IWebHostEnvironment hostingEnvironment) { if (!hostingEnvironment.IsEnvironment("Development")) { var builtConfig = configurationBuilder.Build(); var keyVaultEndpoint = $"https://{builtConfig["KeyVaultConfigOption:Name"]}.vault.azure.net/"; var clientId = builtConfig["KeyVaultConfigOption:ClientId"]; var thumbprint = builtConfig["KeyVaultConfigOption:Thumbprint"]; var tenantId = builtConfig["KeyVaultConfigOption:TenantId"]; var certificate = GetCertificate(thumbprint); var clientCertificateCredential = new ClientCertificateCredential(tenantId, clientId, certificate); configurationBuilder.AddAzureKeyVault(new Uri(keyVaultEndpoint), clientCertificateCredential); } return configurationBuilder; } private static X509Certificate2 GetCertificate(string thumbprint) { var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); var cert = store.Certificates.OfType<X509Certificate2>() .FirstOrDefault(x => x.Thumbprint == thumbprint); store.Close(); if (cert == null) throw new InvalidOperationException($"Failed to find the certificate for thumbprint:{thumbprint}"); return cert; } }
Program.cs
var builder = WebApplication.CreateBuilder(args); { // Environment configuration var configuration = builder.Configuration; var env = builder.Environment; configuration.AddAppConfiguration(env); // Add azure key vault configuration builder.Configuration.AddAzureKeyVaultConfiguration(env); builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>()); builder.Services.AddHttpClient(); builder.Services.AddApplication(); builder.Services.AddInfrastructure(builder.Configuration); builder.Services.AddHelpers(); builder.Services.AddControllers() // Configures the JSON serialization options for controllers. .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; }); builder.Services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; }); } var app = builder.Build(); { app.UseHttpsRedirection(); // Authentication & Authorization app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.UseRouting(); app.UseSwaggerWithVersioning(); } if (app.Environment.IsDevelopment()) { //DEV configurations } app.Run();
appsettings.json
"ServiceBusConfigOption": { "ConnectionString": "@Microsoft.KeyVault(SecretUri={SecretURL}/)", "TopicsEmailTemplate": "EmailTemplates", "TopicUserSetting": "UserSettings" }
注入代码
public class AzureServiceBusFactory : IMessageBusFactory { private readonly ServiceBusConfigOption _serviceBusConfigOption; public AzureServiceBusFactory(IOptions<ServiceBusConfigOption> configurationSettings) { _serviceBusConfigOption = configurationSettings.Value; } }
问题原因与解决方案
核心问题
当前使用的AddAzureKeyVault方法仅会将Key Vault中的密钥直接加载到配置根节点,不会自动解析配置文件中@Microsoft.KeyVault(...)格式的引用。这种引用格式是Azure App Service/Azure App Configuration的特有功能,原生.NET配置系统不支持自动解析。
解决方案步骤
1. 启用Key Vault密钥引用解析
修改AddAzureKeyVaultConfiguration方法,使用SecretManagerOptions配置密钥加载器,实现引用解析:
public static IConfigurationBuilder AddAzureKeyVaultConfiguration(this IConfigurationBuilder configurationBuilder, IWebHostEnvironment hostingEnvironment) { if (!hostingEnvironment.IsEnvironment("Development")) { var builtConfig = configurationBuilder.Build(); var keyVaultEndpoint = $"https://{builtConfig["KeyVaultConfigOption:Name"]}.vault.azure.net/"; var clientId = builtConfig["KeyVaultConfigOption:ClientId"]; var thumbprint = builtConfig["KeyVaultConfigOption:Thumbprint"]; var tenantId = builtConfig["KeyVaultConfigOption:TenantId"]; var certificate = GetCertificate(thumbprint); var credential = new ClientCertificateCredential(tenantId, clientId, certificate); var secretClient = new SecretClient(new Uri(keyVaultEndpoint), credential); // 配置密钥管理器,启用引用解析 configurationBuilder.AddAzureKeyVault( secretClient, new SecretManagerOptions { // 自动解析@Microsoft.KeyVault格式的引用 SecretLoader = new SecretClientSecretLoader(secretClient), ReloadInterval = TimeSpan.FromMinutes(5) }); } return configurationBuilder; }
2. 调整配置加载顺序与构建逻辑
确保Key Vault配置加载后重新构建配置,让解析后的引用生效:
var builder = WebApplication.CreateBuilder(args); { var env = builder.Environment; // 加载基础配置文件 builder.Configuration.AddAppConfiguration(env); // 添加Key Vault配置 builder.Configuration.AddAzureKeyVaultConfiguration(env); // 重新构建配置,应用Key Vault的解析结果 var resolvedConfig = builder.Configuration.Build(); // 使用解析后的配置注册服务 builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>()); builder.Services.AddHttpClient(); builder.Services.AddApplication(); builder.Services.AddInfrastructure(resolvedConfig); // ... 其他服务注册代码 }
3. 验证基础配置正确性
- 确认证书已安装在目标环境的
CurrentUser\My证书存储中,指纹为大写无空格字符串,与配置一致。 - 检查Azure AD应用(对应ClientId)拥有Key Vault的
Secret Get权限,且已在Key Vault访问策略中配置。 - 确保
{SecretURL}是Key Vault密钥的完整URI(例如https://yourvault.vault.azure.net/secrets/ServiceBusConnectionString/abc123)。
4. 替代方案:手动解析配置值
若不想依赖扩展包,可在读取配置时手动替换引用:
public class AzureServiceBusFactory : IMessageBusFactory { private readonly ServiceBusConfigOption _serviceBusConfigOption; public AzureServiceBusFactory(IOptions<ServiceBusConfigOption> configurationSettings, IConfiguration configuration) { _serviceBusConfigOption = configurationSettings.Value; var connectionString = _serviceBusConfigOption.ConnectionString; // 手动解析Key Vault引用格式 if (connectionString.StartsWith("@Microsoft.KeyVault(SecretUri=")) { var secretUri = connectionString.Replace("@Microsoft.KeyVault(SecretUri=", "").TrimEnd(')'); // 从配置中获取Key Vault加载的密钥值(密钥名称为URI最后一段) var secretName = secretUri.Split('/').Last(); _serviceBusConfigOption.ConnectionString = configuration[secretName]; } } }
关键注意事项
- 原生.NET配置系统不支持自动解析
@Microsoft.KeyVault格式,需通过扩展包或手动代码实现。 - 若使用Key Vault直接加载密钥,可简化appsettings.json配置为
"ConnectionString": "ServiceBusConnectionString",确保Key Vault中存在同名密钥即可。
内容的提问来源于stack exchange,提问作者Rakesh Kumar
相关产品推荐
相关产品推荐

