【问题标题】:asp.net identity userName is unique?asp.net 身份用户名是唯一的吗?
【发布时间】:2014-05-26 12:53:45
【问题描述】:

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

据我所知,Id 是关键,而 userName 不是关键,并且定义说它可以为空, 所以我在问自己...为什么在 MVC5 项目模板中,当您输入一个已经存在的用户名时,您会收到一条错误消息??

我尝试访问用户名验证,但无法访问。

这是数据库定义:

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 身份概念的体验,并将改进我的代码。

非常感谢任何指导

【问题讨论】:

  • 我认为试图击败 Identity 接受重复的用户名是一种非常糟糕和危险的方法。在几乎所有的身份验证系统中,用户名都必须是唯一的。一个用户名只能用于验证一个用户。仅仅因为它可以在设计不佳的表格中是唯一的,并不意味着它在概念和实践上就不必是唯一的。

标签: c# asp.net validation asp.net-identity


【解决方案1】:

当我查看 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!?

【讨论】:

    【解决方案2】:

    这发生在 IdentityDbContext 中,您的 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 继承。

    【讨论】:

    • 我使用了 .NET Reflector,但如果您有兴趣自己尝试反编译,DotPeek 是免费的。它往往工作得很好,因为即使在编译之后也会保留大量元数据。
    • 但为什么 IdentityDbContext 会覆盖检查?这样做有什么好处?
    • @flexxxit 好吧,好处似乎很明显。但是,如果您的意思是为什么它不使用数据库约束,也许它可以与更多的后端数据库一起使用?设计时没有人征求我的意见。
    • Core 3.1 中不存在覆盖,请参阅此问题以了解在那里做什么:stackoverflow.com/questions/59089151/…
    • @philw 是的,这个答案是在 .NET Core 甚至存在之前编写的。
    猜你喜欢
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-03
    相关资源
    最近更新 更多