ASP.NET 核心身份 - 扩展密码哈希器

ASP.NET Core Identity - Extending Password Hasher

我正在努力将应用程序从 Web 窗体迁移到 MVC,并选择使用 ASP.NET Core 与 MVC 6 一起使用。

在我当前的应用程序中,我有一个与身份一起使用的自定义密码哈希器。在我的自定义 UserManager class:

中实现非常简单
public ApplicationUserManager()
  : base(new UserStore<IdentityUser>(new AuthContext()))
{
    this.PasswordHasher = new SqlPasswordHasher();
}

我正在尝试对 .NET Core 执行相同的操作,但 UserManager 中不存在 PasswordHasher 属性。我看到构造函数将采用一个 IPasswordHasher 参数,所以我尝试了这个:

public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IServiceProvider serviceProvider, ILogger<UserManager<ApplicationUser>> logger)
  : base(store, optionsAccessor, new SqlPasswordHasher(), userValidators, passwordValidators, keyNormalizer, errors,
        serviceProvider, logger)
{
}

在 SqlPasswordHasher 中,我只是重写了如下所示的 VerifyHashedPassword 方法:

public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
{
    // My custom logic is here
    ...
}

但是,上面的方法不起作用。我在 SqlPasswordHasher 的 VerifyHashedPassword 方法中设置了一个断点,但它没有被触发。

我以为我做错了,我应该利用 DI 来完成它。我更新了我的用户管理器的构造函数,这样它就不会实例化一个新的 SqlPasswordHasher,而是使用默认的接口参数:

public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IServiceProvider serviceProvider, ILogger<UserManager<ApplicationUser>> logger)
  : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
        serviceProvider, logger)
{
}

然后在Startup.cs我添加了一个作用域服务:

services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();

但同样,这不起作用,并且永远不会触发 SqlPasswordHasher 中的断点。

我的自定义登录管理器也有类似的行:

services.AddScoped<SignInManager<ApplicationUser>, ApplicationSignInManager>();

效果很好。 ApplicationSignInManager 带有一个 UserManager 参数,我可以看到 UserManager 带有一个 IPasswordHasher 参数。

我假设 SignInManager 使用 UserManager,而 UserManager 使用 PasswordHasher。所以我的问题是,如何让 UserManager 使用我的自定义密码哈希器?或者,如果不是这种情况,我如何让 SignInManager 使用我的密码哈希器?

编辑:我已经能够确认,当我的 ApplicationUserManager 被实例化时,我的 SqlPasswordHasher 正在构造函数中使用,因此 DI 正常工作。我只是想不通为什么我的 VerifyHashedPassword 覆盖没有被触发。

事实证明问题根本与代码无关。通过

将我的 SqlPasswordHasher 添加到服务中
services.AddScoped<IPasswordHasher<ApplicationUser>, SqlPasswordHasher>();

工作完美。

问题出在我迁移数据的方式上。由于我使用的是与旧版本身份一起使用的现有数据库,因此我必须将以下字段添加到我现有的 AspNetUsers table:

NormalizedUserName
ConcurrencyStamp
LockoutEnd
NormalizedEmail

但是我没有填充 NormalizedUserName 或 NormalizedEmail 字段。所以这就是为什么它永远不会触发我对 VerifyHashedPassword 的覆盖;因为它从未找到我的用户,因为它是根据 NormalizedUserName 查找的。

填充这些字段后,它开始触发我的 VerifyHashedPassword 方法。