依赖注入 ASP.NET:在运行时之前不知道依赖关系

Dependency Injection ASP.NET : Unaware about the dependencies until Runtime

我正在使用 ASP.NET Core 并使用 DI 构建哈希功能。因为我不知道这个阶段使用的散列类型(我们将它存储在持久存储中)。

ICrypto 合约

public interface ICrypto
{
    string HashPassword(string plainPassword);
    bool VerifyHashedPassword(string hashedPassword, string providedPassword);     
}

我有几个 ICrypto 的实现,它们只是包装其他库并提供 ICrypto 签名的实现。例如:

现在,在 UserService 中,我注入 ICyrpto 来散列密码,例如:

Public class UserService
{
     ICrypto _crypto;

     public UserService(ICrypto crypto)
     {
         _crypto = crypto;
     }

    public bool Login (string username, string password)
    {   
        //code omitted
         var hash = _crypto.HashPassword(password);
    }
}

在启动时向容器添加依赖项class

//get encryption type stored in cache, db or somewhere
   var cryptoType = //get param
   if (cryptoType  = "SHA1")
   {
       services.AddTransient<ICrypto, CryptoSHA1>();
   }
   else if (cryptoType  = "MD5")
   {
       services.AddTransient<ICrypto, CryptoMD5>();
   }

我正在寻找一种根据最佳实践执行此操作的方法,并将反映 提到的内容。

如果您从数据库中获取的 cryptoType 值在应用程序的生命周期内保持不变(这意味着,如果您想更改它,您可以重新启动应用程序),这意味着cryptoType 是一个配置值,您可以按照您的描述简单地连接您的应用程序:

var cryptoType = //get param
if (cryptoType = "SHA1")
{
    services.AddTransient<ICrypto, CryptoSHA1>();
}
else if (cryptoType = "MD5")
{
    services.AddTransient<ICrypto, CryptoMD5>();
}

但是,如果您需要动态交换实现(我发现在您的特定情况下这不太可能,但为了论证我们假设),解决方案是实现代理并包装真正的实现。示例:

public interface DatabaseCryptoSelectorProxy : ICrypto
{
    private readonly CryptoSHA1 sha;
    private readonly CryptoMD5 md5;

    public DatabaseCryptoSelectorProxy(CryptoSHA1 sha, CryptoMD5 md5) {
        this.sha = sha;
        this.md5 = md5;
    }

    public string HashPassword(string plainPassword) =>
        GetCrypto().HashPasswords(plainPassword);

    public bool VerifyHashedPassword(string hashedPassword, string providedPassword) =>
        GetCrypto().VerifyHashedPassword(hashedPassword, providedPassword);

    private ICrypto GetCrypto() {
        var cryptoType = // get param
        if (cryptoType = "SHA1") return this.sha;
        if (cryptoType = "MD5") return this.md5;
        throw new InvalidOperationException("Unknown cryptotype: " + cryptotype);
    }
}

此代理有几个明显的优势:

  • 它让 ICrypto 的消费者忘记了一些复杂的调度是基于数据库中的一些数据而发生的。
  • 它避免了在对象图构造期间必须执行此查询,因为这会使该过程不可靠且难以验证。

从安全角度来看,关于密码散列设计的一些注意事项。我没有看到任何强有力的理由以您的方式从加密方法切换,尤其是切换到像 MD5 这样的算法。相反,我建议以 Rfc2898DeriveBytes. An example of how to do this can be shown here 的形式使用 PBKDF2。通过将哈希迭代次数连接到哈希密码(例如通过简单地执行 + "|" + iterations),您稍后可以通过跟上行业标准来增加使用的迭代次数,它允许您自动重新哈希用户的如果您检测到使用的迭代次数是旧值,则登录密码如果他的号码。

此外,如果您认为您需要远离 PBKDF2,您可以在散列前加上使用的算法,这样您就可以再次使用代理,将散列密码传递给正确的实现基于算法前缀。通过将算法存储在数据库的密码哈希中,您可以透明地迁移,而不必一次转换所有现有密码(这是不可能的,因为您无法解密哈希密码)。