我有一个 ASP.NET Core 6.0 应用程序以及一个 SPA 应用程序。两者都使用一个 API,并且是 OIDC IdentityProvider 的客户端。
目前,用户开始使用用户名登录。它是默认 ASP.NET 标识模型中的用户名。我想更改应用程序以使用电子邮件作为登录凭据并使用户名不唯一。
我偶然发现模型中的用户名应该是唯一的,这是不可定制的行为。有什么方法可以使用户名不唯一以及允许用户使用电子邮件登录。这个问题有什么通用的方法吗?
我没有尝试太多。我发现 ASP.NET 身份模型中的用户名是唯一的,这是我不想要的。我在后端方面没有太多经验,所以很早就问了这个问题。
有什么方法可以使用户名不唯一并允许 用户使用电子邮件登录。有什么共同的方法吗 问题?
是的,你是对的。在asp.net core Identity中,IdentityUser的UserName是唯一的,我们无法修改。
针对你的问题,你可以添加一个自定义属性来存储非唯一的用户名,例如DisplayName,并让这个属性非唯一。然后,要允许用户使用电子邮件登录,您应该使电子邮件具有唯一性。之后更改登录页面以使用电子邮件登录并设置用户名和显示名称。请参考以下示例。
创建一个 Asp.net Core 应用程序并使用 Scaffold Identity 添加身份页面:注册、登录页面。
从 Register.cshtml.cs 页面,我们可以看到它将使用 SetUserNameAsync 和 SetEmailAsync 方法设置用户的 UserName 和 Email,默认情况下 email 和 UserName 相同。
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
Add custom user data (DisplayName) to Identity.
创建一个 ApplicationUser 类来添加 DisplayName:
public class ApplicationUser:IdentityUser
{
public string DisplayName { get; set; }
}
在ApplicationDbContext中添加Dbset:
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
更改 Program.cs 文件中的身份服务:
builder.Services.AddDefaultIdentity<ApplicationUser>(options => {
options.SignIn.RequireConfirmedAccount = true;
options.User.RequireUniqueEmail = true; //set email unique
})
在 _LoginPartial.cshtml 和 Identity 相关页面中,将 IdentityUser 替换为 ApplicationUser。
之后启用迁移以更新数据库:
Add-migration AddDisplayName
Update-database
然后,AspNetUsers表如下:
更改注册页面以使用电子邮件和显示名称注册用户
在 Register.cshtml.cs 文件中,在 InputModel 中添加 DisplayName 属性。
在 Register.cshtml 页面中,为 DisplayName 属性添加一个文本框。
在Register页面的OnPostAsync方法中,可以参考下面的代码设置UserName、DisplayName和Email。
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
user.DisplayName = Input.DisplayName;
//generate a unique UserName.
var uniqueusername = $"{Input.DisplayName}_{Guid.NewGuid().ToString("N")}";
await _userStore.SetUserNameAsync(user, uniqueusername, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user, Input.Password);
登录页面OnPostAsync方法中:
使用UserManager.FindByEmailAsync(String)Method方法先找到用户,然后使用SignInManager.PasswordSignInAsync(TUser, String, Boolean, Boolean)方法登录。
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user !=null)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(user, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
结果如下: