cha*_*adb 5 entity-framework azure azure-keyvault azure-managed-identity
I\xe2\x80\x99m 尝试使用 Azure Key Vault 存储实体框架的 Web api 连接字符串。理想情况下,我\xe2\x80\x99d 希望避免将 Key Vault nuget 包与我的数据访问代码耦合。我的 dbContext 类有两个构造函数:
\n\npublic MyDbContext() : base("DefaultConnection")\n{ . . . }\n\npublic MyDbContext(string connectionString) : base(connectionString)\n{ . . . }\nRun Code Online (Sandbox Code Playgroud)\n\n我的代码使用无参数构造函数,它从 Web 配置获取连接字符串。在某些地方,我实例化了一个新的 MyDbContext 对象,这禁止使用注入的解决方案。
\n\n我采取的方法是使用连接字符串定位器在 dbcontext 上设置静态属性:
\n\npublic interface IConnectionStringLocator\n{ string Get(); }\n\npublic class DefaultConnectionStringLocator : IConnectionStringLocator\n{\n public string Get()\n {\n return "DefaultConnection";\n }\n}\n\npublic static IConnectionStringLocator ConnectionStringLocator { get; set; } =\n new DefaultConnectionStringLocator();\nRun Code Online (Sandbox Code Playgroud)\n\n我的 Web api 项目有用于检索密钥保管库机密的 nuget 包。所以在我的 Global.asax 文件中我有这个:
\n\nprotected void Application_Start()\n{\n MyDbContext.ConnectionStringLocator = new ConnectionStringLocator("DefaultConnection");\n}\n\npublic class ConnectionStringLocator : IConnectionStringLocator\n{\n private readonly string _connectionStringName;\n\n public ConnectionStringLocator(string connectionStringName)\n {\n this._connectionStringName = connectionStringName;\n }\n public string Get()\n {\n var keyVaultName = WebConfigurationManager.AppSettings.Get("KeyVaultName");\n if (keyVaultName == "develop")\n return _connectionStringName;\n else\n {\n AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();\n var keyVaultClient =\n new KeyVaultClient(\n new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));\n var defaultConnectionSecret =\n keyVaultClient.GetSecretAsync($"https://{keyVaultName}.vault.azure.net/secrets/{this._connectionStringName}");\n\n return defaultConnectionSecret.Result.Value;\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我发布了这个并且它有效,但它“感觉”不正确。
\n\n另一种选择是遵循这篇文章https://blog.falafel.com/keeping-secrets-with-azure-key-vault/,但这需要我将 KeyVault API 包与我的数据访问结合起来。
\n\n我正在寻找反馈和方向。我应该补充一点,我想使用 Key Vault 的原因是因为它允许我拥有 Azure 管理员,他们可以在线查看应用程序设置,而无需通过连接字符串访问 SQL 数据库。
\n\n具有新 MSI 实施的 KeyVault 资源: https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet/
\n这是我解决这个问题的方法,以防其他人偶然发现它。
创建了一个 ConfigurationManager 类,它首先尝试从密钥保管库获取值,但失败时它使用 WebConfigurationManager 读取应用程序设置。
public static class ConfigurationManager
{
public static string KeyVaultName => WebConfigurationManager.AppSettings.Get("KeyVaultName");
private static readonly Dictionary<string, string> ConfigurationCache = new Dictionary<string, string>();
private static SecretBundle GetSecret(string secretName, string vaultName = null)
{
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient =
new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secretUri = $"https://{vaultName ?? KeyVaultName}.vault.azure.net/secrets/{secretName}";
var secret = keyVaultClient.GetSecretAsync(secretUri);
return secret.Result;
}
public static string GetAppSettingValue(string secretName, string vaultName = null)
{
vaultName = vaultName ?? KeyVaultName;
string key = $"{vaultName}:{secretName}";
string value;
if (ConfigurationCache.TryGetValue(key, out value))
return value;
if (string.IsNullOrEmpty(vaultName) || vaultName == "develop")
{
value = WebConfigurationManager.AppSettings.Get(secretName);
ConfigurationCache.Add(key, value);
return value;
}
var secret = GetSecret(secretName);
value = secret.Value;
ConfigurationCache.Add(key, value);
return value;
}
public static void SetAppSettingValue(string secretName, string value, string vaultName = null)
{
vaultName = vaultName ?? KeyVaultName;
string key = $"{vaultName}:{secretName}";
if (ConfigurationCache.ContainsKey(key))
ConfigurationCache[key] = value;
else
{
WebConfigurationManager.AppSettings[key] = value;
ConfigurationCache.Add(key, value);
}
}
public static string GetConnectionStringValue(string secretName, string vaultName = null)
{
vaultName = vaultName ?? KeyVaultName;
string key = $"{vaultName}:{secretName}";
string value;
if (ConfigurationCache.TryGetValue(key, out value))
return value;
if (string.IsNullOrEmpty(vaultName) || vaultName == "develop")
{
value = WebConfigurationManager.ConnectionStrings[secretName].ConnectionString;
ConfigurationCache.Add(key, value);
return value;
}
var secret = GetSecret(secretName);
value = secret.Value;
ConfigurationCache.Add(key, value);
return value;
}
}
Run Code Online (Sandbox Code Playgroud)
然后在我的 dbcontext 类中,我调用 Configurationmanager.GetConnectionStringValue("DefaultConnection")。
public MyDbContext()
: base(ConfigurationManager.GetConnectionStringValue("DefaultConnection"))
{...}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6304 次 |
| 最近记录: |