我已经重写了一系列在 ASP.NET Identity 2.0 中使用的类,以便它们使用电子邮件地址字段而不是电子邮件字段。
当尝试创建用户时,它说
The UserName field is required.
我尝试覆盖我的ApplicationUser
类中的UserName字段,该类继承自IdentityUser
和[Index(IsUnique = false)]
,但这只会导致唯一约束和非唯一索引已创建。
如何重写
IdentityUser
类以使用户名可为空且非唯一,因为我不想使用它。
注意: 我完全知道您可以使用
AllowOnlyAlphanumericUserNames
类中的 UserValidator
属性将电子邮件存储在用户名字段中。我真的不喜欢这种方法,只想删除这个限制,这样我就可以在没有用户名的情况下存储记录。我将修改我需要的使用用户名查看电子邮件地址的任何功能。
摆脱它的唯一真正方法是实现你自己的
IUserStore
。这听起来像是大量的工作却几乎没有实际收益。
请参阅 UserManager 有方法
FindByNameAsync()
并且 IUserStore<TUser, in TKey>
还要求方法 Task<TUser> FindByNameAsync(string userName)
存在于实现类中。
此外,您还需要创建自己的
IdentityDbContext
实现,或者在 ApplicationDbContext
中创建所需的表/关系以删除唯一性验证。这也是另一项工作负担。
这根本不值得。
我找到了最好的解决方案,只需在使用用户管理器之前清除用户验证器并编写自己的逻辑即可。无需重写任何类。
UserManager<ApplicationUser> userManager,
userManager.UserValidators.Clear();
我回答这个问题有点晚了,但我刚刚遇到这个问题,因为我需要允许用户仅使用他们的电话号码注册(没有用户名,没有电子邮件),同时还保持使用用户名的“正常”注册/电子邮件和密码。
这是我解决问题的方法(示例代码假设
User
是您的自定义 IdentityUser
类)。
IUserValidator<TUser>
实现。您可以直接实现接口或子类UserValidator<TUser>
:public sealed class CustomUserValidator : UserValidator<User>
{
public override async Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user)
{
var result = await base.ValidateAsync(manager, user);
if (result.Succeeded)
{
return result;
}
var errors = result.Errors.ToList();
if (user.UserName is null)
{
var expectedError = Describer.InvalidUserName(user.UserName);
errors.RemoveAll(x => x.Code == expectedError.Code);
}
if (user.Email is null)
{
var expectedError = Describer.InvalidEmail(user.Email);
errors.RemoveAll(x => x.Code == expectedError.Code);
}
return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
}
}
IUserClaimsPrincipalFactory<TUser>
,因为默认实现在尝试将其添加为声明之前不会检查用户名是否为 null
,这会导致异常:public sealed class CustomClaimsPrincipalFactory : IUserClaimsPrincipalFactory<User>
{
public IdentityOptions Options { get; }
public UserManager<User> UserManager { get; }
public CustomClaimsPrincipalFactory(IOptions<IdentityOptions> options, UserManager<User> userManager)
{
Options = options.Value;
UserManager = userManager;
}
public async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claims = await GenerateClaimsAsync(user);
return new ClaimsPrincipal(claims);
}
private async Task<ClaimsIdentity> GenerateClaimsAsync(User user)
{
var userId = user.Id.ToString();
var claims = new ClaimsIdentity(
IdentityConstants.ApplicationScheme,
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
claims.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
if (user.UserName is not null)
{
claims.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, user.UserName));
}
if (UserManager.SupportsUserSecurityStamp)
{
claims.AddClaim(new Claim(
Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
if (UserManager.SupportsUserClaim)
{
claims.AddClaims(await UserManager.GetClaimsAsync(user));
}
// Add more as needed
return claims;
}
}
AddIdentity
扩展方法之前注册您的服务:
services.AddScoped<IUserValidator<User>, CustomUserValidator>();
services.AddScoped<IUserClaimsPrincipalFactory<User>, CustomClaimsPrincipalFactory>();
之后,您可以使用空用户名调用UserManager.CreateAsync
。