IdentityUser 的 IUserValidator
IUserValidator for IdentityUser
我有身份用户管理器的自定义用户电子邮件验证器。
它是根据附加的 属性 IsDeleted(新用户可以使用已删除用户的电子邮件添加)创建的,用于验证用户电子邮件。
这是它的样子。
public class UserEmailValidator<TUser> : IUserValidator<TUser>
where TUser : UserAuth
{
private readonly IUnitOfWork _unitOfWork;
public UserEmailValidator(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
{
var errors = new List<IdentityError>();
var existingAccount = await _unitOfWork.AuthUsers.Get()
.FirstOrDefaultAsync(u => u.NormalizedEmail == user.Email.ToUpper() && !u.IsDeleted);
if (existingAccount != null)
errors.Add(new IdentityError() { Code = GlobalData.Translations.IdentityKeys.DuplicateEmail });
return errors.Any()
? IdentityResult.Failed(errors.ToArray())
: IdentityResult.Success;
}
}
当我使用方法 var res1 = await userManager.CreateAsync(newUser)
时,它按预期工作。
res1 的值为成功。
但实际上当用户分配给角色时也会调用 ValidateAsycn 方法
var res2 = await userManager.AddToRoleAsync(newUser, "admin")
并且 res2 的结果是失败的,因为有重复的电子邮件。
有没有办法阐明要验证的操作(用户创建、添加到角色或任何其他类型的操作),以便为所有情况提供正确的验证?
这是来自 Startup.cs
的设置
var userBuilder = services.AddIdentity<UserAuth, Role>(options =>
{
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
options.ClaimsIdentity.UserIdClaimType = GlobalData.CustomClaimNames.UserId;
}).AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserValidator<UserEmailValidator<UserAuth>>()
.AddDefaultTokenProviders()
.AddEmailAndPasswordConfimationTotpTokenProvider();
更新:
await _userManager.UpdateAsync(user);
await _userManager.RemovePasswordAsync(user);
也调用相同的 ValidateAsync 方法
Is there a way to clarify what the action is going to be validated...?
没有调用 ValidateUser
的上下文,除了 User
本身,这意味着没有真正的方法可以确切地知道 为什么 它正在调用。
UserManager
包含一个 protected
方法 (ValidateAsync
),每当调用 UserManager.CreateAsync
或 UserManager.UpdateUserAsync
时都会在内部调用该方法。对 AddToRoleAsync
的调用会导致对 UpdateUserAsync
的调用,最终会通过 IUserValidator<TUser>
的实现以 ValidateAsync
运行 来执行验证。
执行 built-in UserValidator<TUser>
that you've replaced handles your problem with the following check (source):
var owner = await manager.FindByEmailAsync(email);
if (owner != null &&
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{
errors.Add(Describer.DuplicateEmail(email));
}
首先,它会检查是否存在使用该电子邮件地址的现有帐户,然后检查该帐户是否与正在验证的帐户相同。如果相同,则不是重复。
这一切都意味着您应该能够扩展您的检查以执行类似的逻辑。例如:
var existingAccount = await _unitOfWork.AuthUsers.Get().FirstOrDefaultAsync(u =>
u.NormalizedEmail == user.Email.ToUpper() &&
!u.IsDeleted &&
u.Id != user.Id);
在这里,我添加了 u.Id != user.Id
,它比 built-in 实现要简单得多,因为您的代码知道它正在使用 UserAuth
并且可以直接使用它的属性。
我有身份用户管理器的自定义用户电子邮件验证器。
它是根据附加的 属性 IsDeleted(新用户可以使用已删除用户的电子邮件添加)创建的,用于验证用户电子邮件。
这是它的样子。
public class UserEmailValidator<TUser> : IUserValidator<TUser>
where TUser : UserAuth
{
private readonly IUnitOfWork _unitOfWork;
public UserEmailValidator(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
{
var errors = new List<IdentityError>();
var existingAccount = await _unitOfWork.AuthUsers.Get()
.FirstOrDefaultAsync(u => u.NormalizedEmail == user.Email.ToUpper() && !u.IsDeleted);
if (existingAccount != null)
errors.Add(new IdentityError() { Code = GlobalData.Translations.IdentityKeys.DuplicateEmail });
return errors.Any()
? IdentityResult.Failed(errors.ToArray())
: IdentityResult.Success;
}
}
当我使用方法 var res1 = await userManager.CreateAsync(newUser)
时,它按预期工作。
res1 的值为成功。
但实际上当用户分配给角色时也会调用 ValidateAsycn 方法
var res2 = await userManager.AddToRoleAsync(newUser, "admin")
并且 res2 的结果是失败的,因为有重复的电子邮件。 有没有办法阐明要验证的操作(用户创建、添加到角色或任何其他类型的操作),以便为所有情况提供正确的验证?
这是来自 Startup.cs
var userBuilder = services.AddIdentity<UserAuth, Role>(options =>
{
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
options.ClaimsIdentity.UserIdClaimType = GlobalData.CustomClaimNames.UserId;
}).AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserValidator<UserEmailValidator<UserAuth>>()
.AddDefaultTokenProviders()
.AddEmailAndPasswordConfimationTotpTokenProvider();
更新:
await _userManager.UpdateAsync(user);
await _userManager.RemovePasswordAsync(user);
也调用相同的 ValidateAsync 方法
Is there a way to clarify what the action is going to be validated...?
没有调用 ValidateUser
的上下文,除了 User
本身,这意味着没有真正的方法可以确切地知道 为什么 它正在调用。
UserManager
包含一个 protected
方法 (ValidateAsync
),每当调用 UserManager.CreateAsync
或 UserManager.UpdateUserAsync
时都会在内部调用该方法。对 AddToRoleAsync
的调用会导致对 UpdateUserAsync
的调用,最终会通过 IUserValidator<TUser>
的实现以 ValidateAsync
运行 来执行验证。
执行 built-in UserValidator<TUser>
that you've replaced handles your problem with the following check (source):
var owner = await manager.FindByEmailAsync(email);
if (owner != null &&
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{
errors.Add(Describer.DuplicateEmail(email));
}
首先,它会检查是否存在使用该电子邮件地址的现有帐户,然后检查该帐户是否与正在验证的帐户相同。如果相同,则不是重复。
这一切都意味着您应该能够扩展您的检查以执行类似的逻辑。例如:
var existingAccount = await _unitOfWork.AuthUsers.Get().FirstOrDefaultAsync(u =>
u.NormalizedEmail == user.Email.ToUpper() &&
!u.IsDeleted &&
u.Id != user.Id);
在这里,我添加了 u.Id != user.Id
,它比 built-in 实现要简单得多,因为您的代码知道它正在使用 UserAuth
并且可以直接使用它的属性。