IUserValidator for IdentityUser

时间:2019-05-09 17:21:43

标签: c# asp.net-core asp.net-core-identity

我具有用于身份用户管理器的自定义用户电子邮件验证程序。

它是根据附加属性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方法

1 个答案:

答案 0 :(得分:1)

  

有没有办法澄清将要验证的操作...?

除了ValidateUser本身之外,没有调用User的上下文,这意味着没有真正的方法确切地知道为什么为什么被调用。

UserManager包含一个protected方法(ValidateAsync),该方法在每次调用UserManager.CreateAsyncUserManager.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一起使用,并且可以直接使用其属性。