在身份核心中运行自定义用户验证程序之前创建用户
User getting created before custom user validator runs in Identity core
我在 .net core 3.1 web api 中使用 identity core 进行用户管理。现在,我想检查用户的电子邮件,如果它符合要求,那么他才会被创建。下面的代码说明了我想要实现的目标
我有一个自定义用户验证器,如下所示:
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 在我的默认验证中检查唯一的电子邮件。这只是为了进一步发展明确一个概念。
Update 进一步调试后,我看到自定义验证方法被调用了两次。一次在用户创建之前,一次因为某种原因在创建之后。我插入一个新的唯一电子邮件,自定义验证成功通过,在创建用户后,再次调用自定义验证,发现电子邮件已经注册并抛出错误消息。这很奇怪
发现 AddToRoleAsync 再次调用自定义验证器并发现 table 中存在的用户。必须包括检查在 table 中找到的具有相同电子邮件的用户是否与正在更新的用户相同。
代码如下:
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."
}));
}
}
这应该可以帮助很多人
我在 .net core 3.1 web api 中使用 identity core 进行用户管理。现在,我想检查用户的电子邮件,如果它符合要求,那么他才会被创建。下面的代码说明了我想要实现的目标
我有一个自定义用户验证器,如下所示:
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 在我的默认验证中检查唯一的电子邮件。这只是为了进一步发展明确一个概念。
Update 进一步调试后,我看到自定义验证方法被调用了两次。一次在用户创建之前,一次因为某种原因在创建之后。我插入一个新的唯一电子邮件,自定义验证成功通过,在创建用户后,再次调用自定义验证,发现电子邮件已经注册并抛出错误消息。这很奇怪
发现 AddToRoleAsync 再次调用自定义验证器并发现 table 中存在的用户。必须包括检查在 table 中找到的具有相同电子邮件的用户是否与正在更新的用户相同。
代码如下:
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."
}));
}
}
这应该可以帮助很多人