ASP.NET Core身份验证-扩展密码哈希器

6

我正在努力将一个Web Forms应用程序迁移到MVC,并选择使用基于ASP.NET Core的MVC 6。

在我的当前应用程序中,我有一个与Identity一起使用的自定义密码哈希器。在我的自定义UserManager类中,实现非常简单:

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重写没有被触发。


哪一个起作用了?处于同一艘船上。显然v2.0已经注入到用户管理器本身中。一直在尝试在自定义密码验证器中使用它,但没有成功。 - Jay
@Jay,我的代码是有效的。我的问题实际上是数据方面的。我没有在2.0上测试过我的代码,所以我不确定它是否能在那里运行。抱歉。(还有,很抱歉我回复晚了) - Mat H.
没问题。我已经解决了。在我的情况下,只是一个愚蠢的错误。感谢您的消息。 - Jay
1个回答

18

结果证明这个问题完全与代码无关。通过以下方式将我的SqlPasswordHasher添加到服务中:

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

运行完美。

问题出在我迁移数据的方式上。由于我使用的是一个已存在的数据库,该数据库正在使用旧版本的Identity,因此我不得不向现有的AspNetUsers表中添加以下字段:

NormalizedUserName
ConcurrencyStamp
LockoutEnd
NormalizedEmail

然而,我没有填充NormalizedUserName或NormalizedEmail字段。这就是为什么它从未触发我的VerifyHashedPassword重写的原因;因为它从来没有找到我的用户,因为它是基于NormalizedUserName进行查找。

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


我刚刚浪费了几个小时在这上面。非常感谢你发布你的解决方案。 - Scott Hulme
@ScottHulme 没问题,我很高兴它能帮到你。 - Mat H.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接