4

我正在尝试使用 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 包与我的数据访问权限结合起来。

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

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

4

2 回答 2

4

这是我解决这个问题的方法,以防其他人偶然发现它。

创建了一个 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"))
    {...}
于 2017-10-25T21:35:41.357 回答
0

对于 .NET 4.7.1 或更高版本,您可以使用.NET 的配置生成器连接到密钥保管库,只需在 appsettings 或 web.config 文件中添加少量 XML。我已经使用该库以及 XML 转换和 azure devops 来根据环境连接到不同的密钥库。我在我的博客文章中谈到了它。

于 2020-06-05T14:11:22.700 回答