我具有用于身份用户管理器的自定义用户电子邮件验证程序。
它是根据附加属性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的值为Succeed。
但是实际上,当用户分配角色时,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();
UPD:
await _userManager.UpdateAsync(user);
await _userManager.RemovePasswordAsync(user);
以及调用相同的ValidateAsync方法
答案 0 :(得分:1)
有没有办法澄清将要验证的操作...?
除了ValidateUser
本身之外,没有调用User
的上下文,这意味着没有真正的方法确切地知道为什么为什么被调用。
UserManager
包含一个protected
方法(ValidateAsync
),该方法在每次调用UserManager.CreateAsync
或UserManager.UpdateUserAsync
时在内部被调用。对AddToRoleAsync
的调用导致对UpdateUserAsync
的调用,最终以ValidateAsync
贯穿IUserValidator<TUser>
的实现来执行验证。
已替换的内置UserValidator<TUser>
的实现通过以下检查(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
,它比内置实现要少得多,因为您的代码知道它可以与UserAuth
一起使用,并且可以直接使用其属性。