为什么密码被验证两次?
why password is validated twice?
我正在使用自定义验证配置 ASP.NET Core Identity 的密码验证,因此在 startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentity<AppUser, IdentityRole>( opts => {
opts.Password.RequiredLength = 6;
}).AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordValidator>();
...
}
我的客户密码验证器是
public class CustomPasswordValidator : PasswordValidator<AppUser>
{
public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
IdentityResult result = await base.ValidateAsync(manager, user, password);
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
if (password.ToLower().Contains(user.UserName.ToLower()))
{
errors.Add(new IdentityError
{
Code = "PasswordContainsUserName",
Description = "Password cannot contain username"
});
}
return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
}
当我 运行 应用程序并输入长度 < 6 的无效密码时,会出现重复的验证输出:
Passwords must be at least 6 characters.
Passwords must be at least 6 characters.
我猜是因为我调用了base
的ValidateAsync()
(其中包含startup.cs
中的验证登录),但那不是我的CustomPasswordValidator
覆盖base
的ValidateAsync()
,所以base的验证应该只调用一次?
我发现您需要在 service.AddIdentity
之前注册您的 CustomPasswordValidator
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordValidator>();
services.AddIdentity<AppUser, IdentityRole>( opts => {
opts.Password.RequiredLength = 6;
}).AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordValidator>();
此调用不会替换调用AddIdentity
已添加的IPasswordValidator<AppUser>
注册;它 添加另一个 。这意味着您最终会得到 两个 个密码验证器,它们都检查同一组内置规则。
通常,当向 DI 请求类型时,我们会要求一个单一的实现。这是一个示例构造函数:
public SomeClass(ISomeService someService) { }
如果 two 实现已为 ISomeService
注册,此构造函数将获得已注册 last 的实例].但是,我们仍然可以通过更新构造函数来请求 集合 of ISomeService
来获得 both 个实例。这是一个例子:
public SomeClass(IEnumerable<ISomeService> someServices) { }
在这种情况下,ISomeService
的两个已注册实现,someServices
包含 两个 实现的实例。这正是 what happens in UserManager
,它旨在支持多个验证器。
查看 UserManager.ValidatePasswordAsync
的 source 显示验证器是如何按顺序枚举和执行的:
foreach (var v in PasswordValidators)
{
var result = await v.ValidateAsync(this, user, password);
if (!result.Succeeded)
{
errors.AddRange(result.Errors);
}
}
这意味着,CustomPasswordValidator
可以直接实现 IPasswordValidator<AppUser>
而不是扩展 PasswordValidator<AppUser>
:
public class CustomPasswordValidator : IPasswordValidator<AppUser>
{
public async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
// ...
}
}
您的实现方法中的代码保持不变,除了调用 base
.
我正在使用自定义验证配置 ASP.NET Core Identity 的密码验证,因此在 startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentity<AppUser, IdentityRole>( opts => {
opts.Password.RequiredLength = 6;
}).AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordValidator>();
...
}
我的客户密码验证器是
public class CustomPasswordValidator : PasswordValidator<AppUser>
{
public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
IdentityResult result = await base.ValidateAsync(manager, user, password);
List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
if (password.ToLower().Contains(user.UserName.ToLower()))
{
errors.Add(new IdentityError
{
Code = "PasswordContainsUserName",
Description = "Password cannot contain username"
});
}
return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
}
当我 运行 应用程序并输入长度 < 6 的无效密码时,会出现重复的验证输出:
Passwords must be at least 6 characters.
Passwords must be at least 6 characters.
我猜是因为我调用了base
的ValidateAsync()
(其中包含startup.cs
中的验证登录),但那不是我的CustomPasswordValidator
覆盖base
的ValidateAsync()
,所以base的验证应该只调用一次?
我发现您需要在 service.AddIdentity
CustomPasswordValidator
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordValidator>();
services.AddIdentity<AppUser, IdentityRole>( opts => {
opts.Password.RequiredLength = 6;
}).AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
services.AddTransient<IPasswordValidator<AppUser>, CustomPasswordValidator>();
此调用不会替换调用AddIdentity
已添加的IPasswordValidator<AppUser>
注册;它 添加另一个 。这意味着您最终会得到 两个 个密码验证器,它们都检查同一组内置规则。
通常,当向 DI 请求类型时,我们会要求一个单一的实现。这是一个示例构造函数:
public SomeClass(ISomeService someService) { }
如果 two 实现已为 ISomeService
注册,此构造函数将获得已注册 last 的实例].但是,我们仍然可以通过更新构造函数来请求 集合 of ISomeService
来获得 both 个实例。这是一个例子:
public SomeClass(IEnumerable<ISomeService> someServices) { }
在这种情况下,ISomeService
的两个已注册实现,someServices
包含 两个 实现的实例。这正是 what happens in UserManager
,它旨在支持多个验证器。
查看 UserManager.ValidatePasswordAsync
的 source 显示验证器是如何按顺序枚举和执行的:
foreach (var v in PasswordValidators) { var result = await v.ValidateAsync(this, user, password); if (!result.Succeeded) { errors.AddRange(result.Errors); } }
这意味着,CustomPasswordValidator
可以直接实现 IPasswordValidator<AppUser>
而不是扩展 PasswordValidator<AppUser>
:
public class CustomPasswordValidator : IPasswordValidator<AppUser>
{
public async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
{
// ...
}
}
您的实现方法中的代码保持不变,除了调用 base
.