首页 文章

Azure Key Vault与实体框架“DefaultConnection”应用程序设置

提问于
浏览
3

我正在尝试使用Azure Key Vault存储Entity Framework的web api连接字符串 . 理想情况下,我希望避免将密钥库nuget包与我的数据访问代码耦合 . 我的dbContext类有两个构造函数:

public MyDbContext() : base("DefaultConnection")
{ . . . }

public MyDbContext(string connectionString) : base(connectionString)
{ . . . }

我的代码使用无参数构造函数,它从Web配置获取连接字符串 . 在某些地方我实例化了一个新的MyDbContext对象,它禁止使用注入的解决方案 .

我采取的路线是在我的dbcontext上使用连接字符串定位器设置静态属性:

public interface IConnectionStringLocator
{ string Get(); }

public class DefaultConnectionStringLocator : IConnectionStringLocator
{
    public string Get()
    {
        return "DefaultConnection";
    }
}

public static IConnectionStringLocator ConnectionStringLocator { get; set; } =
    new DefaultConnectionStringLocator();

我的web api项目有nuget包,用于检索密钥保险库秘密 . 所以在我的Global.asax文件中我有这个:

protected void Application_Start()
{
    MyDbContext.ConnectionStringLocator = new ConnectionStringLocator("DefaultConnection");
}

public class ConnectionStringLocator : IConnectionStringLocator
{
    private  readonly string _connectionStringName;

    public ConnectionStringLocator(string connectionStringName)
    {
        this._connectionStringName = connectionStringName;
    }
    public string Get()
    {
        var keyVaultName = WebConfigurationManager.AppSettings.Get("KeyVaultName");
        if (keyVaultName == "develop")
            return _connectionStringName;
        else
        {
            AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
            var keyVaultClient =
                new KeyVaultClient(
                    new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
            var defaultConnectionSecret =
                keyVaultClient.GetSecretAsync($"https://{keyVaultName}.vault.azure.net/secrets/{this._connectionStringName}");

            return defaultConnectionSecret.Result.Value;
        }
    }
}

我发布了这个并且它有效,但它并没有“感觉”正确 .

另一种选择是遵循这篇文章https://blog.falafel.com/keeping-secrets-with-azure-key-vault/,但它需要我将KeyVault API包与我的数据访问结合起来 .

我正在寻找反馈和指导 . 我应该补充一点,我想使用密钥保险库的原因是因为它允许我拥有可以在线查看应用程序设置而无需通过连接字符串访问sql数据库的天蓝色管理员 .

具有新MSI实现的KeyVault资源:https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet/

1 回答

  • 1

    以下是我解决这个问题的方法,万一其他人偶然发现它 .

    创建了一个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;
        }
    }
    

    然后在我的dbcontext类中,我调用Configurationmanager.GetConnectionStringValue(“DefaultConnection”) .

    public MyDbContext()
            : base(ConfigurationManager.GetConnectionStringValue("DefaultConnection"))
        {...}
    

相关问题