首页 > 解决方案 > 在自定义用户验证器在身份核心中运行之前创建用户

问题描述

我在 .net core 3.1 web api 中使用身份核心进行用户管理。现在,我想检查用户的电子邮件,如果它满足要求,那么他将被创建。下面的代码告诉了我很多关于我想要实现的目标

我有一个自定义用户验证器,如下所示:

 public class CustomEmailValidator<TUser> : IUserValidator<TUser>
   where TUser : User
{

    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager,
                                              TUser user)
    {
        User userFromEmail = null;

        if(!string.IsNullOrEmpty(user.Email))
            userFromEmail = manager.FindByEmailAsync(user.Email).Result;

        if (userFromEmail == null)
            return Task.FromResult(IdentityResult.Success);

        return Task.FromResult(
        IdentityResult.Failed(new IdentityError
        {
            Code = "Err",
            Description = "You are already registered with us."
        }));
    }
}

我在我的启动中添加了验证器,如下所示:

services.AddDbContext<DataContext>(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

        IdentityBuilder builder = services.AddIdentityCore<User>(opt =>
        {
            
            opt.User.RequireUniqueEmail = false;
            opt.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz0123456789._-";
            opt.Password.RequireDigit = true;
            opt.Password.RequiredLength = 6;
            opt.Password.RequireNonAlphanumeric = true;
            opt.Password.RequireUppercase = true;
            opt.Password.RequireLowercase = true;
        })
        .AddUserValidator<CustomEmailValidator<User>>();
        builder = new IdentityBuilder(builder.UserType, typeof(Role), builder.Services);
        builder.AddEntityFrameworkStores<DataContext>();
        builder.AddRoleValidator<RoleValidator<Role>>();
        builder.AddRoleManager<RoleManager<Role>>();
        builder.AddSignInManager<SignInManager<User>>();

可以看出,我也想使用默认用户验证和我的自定义验证。问题是用户是在默认验证后立即创建的,并且电子邮件总是在我的自定义验证中存在。我真的不想覆盖我的默认验证。

创建用户如下:

    [HttpPost("Register")]
    public async Task<IActionResult> Register(UserForRegisterDto userForRegister)
    {
        var userToCreate = _mapper.Map<User>(userForRegister);

        var result = await _userManager.CreateAsync(userToCreate, userForRegister.Password);

        if (result.Succeeded)
        {
            var roleresult = await _userManager.AddToRoleAsync(userToCreate, "Member");
            return Ok(roleresult);
        }
        return BadRequest(result.Errors);
    }

注意这不是我的实际用例。我知道我可以通过设置 opt.User.RequireUniqueEmail = true 在默认验证中检查唯一电子邮件。这只是为进一步发展明确一个概念。

更新进一步调试后,我看到自定义验证方法被调用了两次。一次在用户创建之前,一次在创建之后出于某种原因。我插入了一封新的唯一电子邮件,并且自定义验证成功通过,在创建用户后,再次调用自定义验证并找到已注册的电子邮件并引发错误消息。这很奇怪

标签: asp.net-coreidentity

解决方案


发现 AddToRoleAsync 再次调用自定义验证器并找到表中存在的用户。必须检查在表中找到的具有相同电子邮件的用户是否与获得更新的用户相同。

下面的代码:

 public class CustomEmailValidator<TUser> : IUserValidator<TUser>
 where TUser : User
{

  public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager,
                                          TUser user)
  {
    User userFromEmail = null;

    if(!string.IsNullOrEmpty(user.Email))
        userFromEmail = manager.FindByEmailAsync(user.Email).Result;

    if (userFromEmail == null)
        return Task.FromResult(IdentityResult.Success);
    else {
            if(string.Equals(userFromEmail.Id, user.Id))
            {
                return Task.FromResult(IdentityResult.Success);
            }
    }

    return Task.FromResult(
    IdentityResult.Failed(new IdentityError
    {
        Code = "Err",
        Description = "You are already registered with us."
    }));
  }
}

这应该可以帮助很多人


推荐阅读