ASP.NET Identity的userName是唯一的吗?

13

我正在阅读有关Microsoft用户身份的内容,并尝试在我的MVC5应用程序中应用它们。

据我所知,Id是关键,而userName不是关键,定义说它可以为null,那么我想知道...为什么在MVC5项目模板中,当您输入已存在的userName时,会收到错误消息?

我试图找到userName验证,但我找不到。

以下是数据库定义:

CREATE TABLE [dbo].[AspNetUsers] (
    [Id]            NVARCHAR (128) NOT NULL,
    [UserName]      NVARCHAR (MAX) NULL,

这里是IdentityUser的定义,注意(没有验证):

namespace Microsoft.AspNet.Identity.EntityFramework
{
    public class IdentityUser : IUser
    {
        public IdentityUser();
        public IdentityUser(string userName);

        public virtual ICollection<IdentityUserClaim> Claims { get; }
        public virtual string Id { get; set; }
        public virtual ICollection<IdentityUserLogin> Logins { get; }
        public virtual string PasswordHash { get; set; }
        public virtual ICollection<IdentityUserRole> Roles { get; }
        public virtual string SecurityStamp { get; set; }
        public virtual string UserName { get; set; }
    }
}

当用户注册时,会调用UserManager.CreateAsync方法,下面是定义:

     public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser() { UserName = model.UserName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    AddErrors(result);
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

而这是我在CreateAsync方面达到的最后一个事项:

public virtual Task<IdentityResult> CreateAsync(TUser user, string password);

我在代码中没有看到任何验证,但它不允许您输入已存在的用户名。

我认为理解这个是如何工作的将会改善我的asp.net身份概念体验,并且会提高我的代码水平。

非常感谢任何指导。


我认为试图让身份验证接受重复的用户名是一种非常糟糕和危险的方法。在几乎所有的身份验证系统中,用户名必须是唯一的。一个用户名只能用于验证一个用户。仅仅因为它可以在设计不良的表中是唯一的,并不意味着它在概念和实践中不必须是唯一的。 - ProfK
2个回答

14

这种情况发生在IdentityDbContext<TUser>中,你的ApplicationDbContext可能会继承它。它重写了DbContext的ValidateEntity方法来进行检查。请参考以下反编译代码:

这是发生在 IdentityDbContext<TUser> 中的事情,你的 ApplicationDbContext 可能是从它那里继承而来。 它重写了 DbContext 的 ValidateEntity 方法来进行检查。 请查看此反编译代码:

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        if ((entityEntry != null) && (entityEntry.State == EntityState.Added))
        {
            TUser user = entityEntry.Entity as TUser;
            if ((user != null) && this.Users.Any<TUser>(u => string.Equals(u.UserName, user.UserName)))
            {
                return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { ValidationErrors = { new DbValidationError("User", string.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, new object[] { user.UserName })) } };
            }
            IdentityRole role = entityEntry.Entity as IdentityRole;
            if ((role != null) && this.Roles.Any<IdentityRole>(r => string.Equals(r.Name, role.Name)))
            {
                return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { ValidationErrors = { new DbValidationError("Role", string.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, new object[] { role.Name })) } };
            }
        }
        return base.ValidateEntity(entityEntry, items);
    }

如果您不希望这种行为,您可以直接从DbContext继承。


1
我使用了.NET Reflector,但是如果你有兴趣尝试反编译,DotPeek是免费的。它通常运行得很好,因为即使在编译后也保留了大量元数据。 - Casey
但是为什么IdentityDbContext<TUser>会覆盖检查呢?这样做有什么好处? - flexxxit
@flexxxit 嗯,好处似乎很明显。但是如果你的意思是为什么不使用数据库约束,也许是为了使其与更多的后端数据库兼容?当设计它时没有人问过我的意见。 - Casey
在Core 3.1中没有覆盖该方法,可以参考以下链接了解如何处理: https://stackoverflow.com/questions/59089151/equivalent-of-validateentity-in-core3-0 - philw
@Casey 那就意味着我无论如何都不能在 .net core 中设置重复的用户名了吗? - Vasanth R
显示剩余2条评论

6
当我查看 ASP.NET Identity 的示例 (https://www.nuget.org/packages/Microsoft.AspNet.Identity.Samples) 时,我注意到他们使用了一个默认设置为 RequireUniqueEmail = true; 的 UserValidator。
该示例使用以下代码将 RequireUniqueEmail 属性设置为 true。
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };
        return manager;
    }

也许这就是你的MVC应用程序中用户名唯一的原因。尝试将该属性设置为false!?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接