Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/33.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Microsoft ASP.NET标识-多个同名用户_C#_Asp.net_Authentication_Forms Authentication_Asp.net Identity - Fatal编程技术网

C# Microsoft ASP.NET标识-多个同名用户

C# Microsoft ASP.NET标识-多个同名用户,c#,asp.net,authentication,forms-authentication,asp.net-identity,C#,Asp.net,Authentication,Forms Authentication,Asp.net Identity,我正在尝试一些我认为非常奇特的东西,我面临一些问题,我希望在StackOverflow上的用户的帮助下可以解决这些问题 故事 我正在写需要认证和注册的申请书。我选择使用Microsoft.AspNet.Identity。我过去没有经常使用它,所以不要在这个决定上评判我 上面提到的框架包含一个users表,其中包含所有注册用户 我创建了一个示例图片来展示应用程序将如何工作 该应用程序由3个不同的组件组成: 后端(WebAPI) 客户(直接使用WebAPI) 最终用户(使用移动应用程序-iOS)

我正在尝试一些我认为非常奇特的东西,我面临一些问题,我希望在StackOverflow上的用户的帮助下可以解决这些问题

故事 我正在写需要认证和注册的申请书。我选择使用
Microsoft.AspNet.Identity
。我过去没有经常使用它,所以不要在这个决定上评判我

上面提到的框架包含一个users表,其中包含所有注册用户

我创建了一个示例图片来展示应用程序将如何工作

该应用程序由3个不同的组件组成:

  • 后端(WebAPI)
  • 客户(直接使用WebAPI)
  • 最终用户(使用移动应用程序-iOS)
  • 所以,我有一个后端,客户可以在上面注册。每个成员有一个唯一的用户,所以这里没有问题。 这里的客户是一家公司,最终用户是该公司的客户

    您可能已经看到了问题,很可能
    User 1
    Customer 1
    的客户,但也可能是
    Customer 2
    的客户

    现在,客户可以邀请会员使用移动应用程序。当客户这样做时,最终用户确实会收到一封电子邮件,其中包含指向活动用户本人的链接

    现在,只要您的用户是唯一的,这一切都可以正常工作,但我确实有一个用户是
    Customer 1
    Customer 2
    的客户。两个客户都可以邀请同一个用户,用户需要注册2次,每个
    客户注册一次

    问题 在
    Microsoft.AspNet.Identity
    框架中,用户应该是唯一的,根据我的情况,我无法管理

    问题 是否可以向
    IdentityUser
    添加额外的参数,以确保用户是唯一的

    我已经做了什么
  • 创建从
    IdentityUser
    继承的自定义类,该类包含应用程序id:

    public class AppServerUser : IdentityUser
    {
        #region Properties
    
        /// <summary>
        ///     Gets or sets the id of the member that this user belongs to.
        /// </summary>
        public int MemberId { get; set; }
    
        #endregion
    }
    
  • 使用框架的已修改调用

    IUserStore<IdentityUser> -> IUserStore<AppServerUser>
    UserManager<IdentityUser>(_userStore) -> UserManager<AppServerUser>(_userStore);
    
    我相信这是一个解决办法 我确实认为我需要更改
    UserManager
    ,但我不确定。 我希望这里有人对这个框架有足够的知识来帮助我,因为它确实阻碍了我们的应用程序开发

    如果这是不可能的,我也想知道,也许你可以告诉我另一个框架,允许我这样做


    注意:我不想自己写一个完整的用户管理,因为这将是对轮子的重新发明。

    首先,我理解你想法背后的想法,因此我将开始解释“为什么”你不能创建多个同名用户

    同名用户名: 您现在遇到的问题与IdentityDbContext有关。如您所见(),identityDbContext设置了关于唯一用户和角色的规则,首先是在模型创建时:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            if (modelBuilder == null)
            {
                throw new ArgumentNullException("modelBuilder");
            }
    
            // Needed to ensure subclasses share the same table
            var user = modelBuilder.Entity<TUser>()
                .ToTable("AspNetUsers");
            user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
            user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
            user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
            user.Property(u => u.UserName)
                .IsRequired()
                .HasMaxLength(256)
                .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UserNameIndex") { IsUnique = true }));
    
            // CONSIDER: u.Email is Required if set on options?
            user.Property(u => u.Email).HasMaxLength(256);
    
            modelBuilder.Entity<TUserRole>()
                .HasKey(r => new { r.UserId, r.RoleId })
                .ToTable("AspNetUserRoles");
    
            modelBuilder.Entity<TUserLogin>()
                .HasKey(l => new { l.LoginProvider, l.ProviderKey, l.UserId })
                .ToTable("AspNetUserLogins");
    
            modelBuilder.Entity<TUserClaim>()
                .ToTable("AspNetUserClaims");
    
            var role = modelBuilder.Entity<TRole>()
                .ToTable("AspNetRoles");
            role.Property(r => r.Name)
                .IsRequired()
                .HasMaxLength(256)
                .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") { IsUnique = true }));
            role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
        }
    
    模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
    {
    if(modelBuilder==null)
    {
    抛出新的ArgumentNullException(“modelBuilder”);
    }
    //需要确保子类共享同一个表
    var user=modelBuilder.Entity()
    .ToTable(“AspNetUsers”);
    user.HasMany(u=>u.Roles).WithRequired().HasForeignKey(ur=>ur.UserId);
    user.HasMany(u=>u.Claims).WithRequired().HasForeignKey(uc=>uc.UserId);
    user.HasMany(u=>u.Logins).WithRequired().HasForeignKey(ul=>ul.UserId);
    user.Property(u=>u.UserName)
    .IsRequired()
    .HasMaxLength(256)
    .HasColumnAnnotation(“索引”,新索引注释(新索引属性(“用户名索引”){IsUnique=true}));
    //考虑:如果设置了选项,则需要发送电子邮件?
    user.Property(u=>u.Email).HasMaxLength(256);
    modelBuilder.Entity()
    .HasKey(r=>new{r.UserId,r.RoleId})
    .ToTable(“AspNetUserRoles”);
    modelBuilder.Entity()
    .HasKey(l=>new{l.LoginProvider,l.ProviderKey,l.UserId})
    .ToTable(“AspNetUserLogins”);
    modelBuilder.Entity()
    .ToTable(“AspNetUserClaims”);
    var role=modelBuilder.Entity()
    .ToTable(“AspNetRoles”);
    属性(r=>r.Name)
    .IsRequired()
    .HasMaxLength(256)
    .HasColumnAnnotation(“索引”,新索引注释(新索引属性(“RoleNameIndex”){IsUnique=true});
    role.HasMany(r=>r.Users).WithRequired().HasForeignKey(ur=>ur.RoleId);
    }
    
    第二,关于验证实体:

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
            IDictionary<object, object> items)
        {
            if (entityEntry != null && entityEntry.State == EntityState.Added)
            {
                var errors = new List<DbValidationError>();
                var user = entityEntry.Entity as TUser;
                //check for uniqueness of user name and email
                if (user != null)
                {
                    if (Users.Any(u => String.Equals(u.UserName, user.UserName)))
                    {
                        errors.Add(new DbValidationError("User",
                            String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, user.UserName)));
                    }
                    if (RequireUniqueEmail && Users.Any(u => String.Equals(u.Email, user.Email)))
                    {
                        errors.Add(new DbValidationError("User",
                            String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateEmail, user.Email)));
                    }
                }
                else
                {
                    var role = entityEntry.Entity as TRole;
                    //check for uniqueness of role name
                    if (role != null && Roles.Any(r => String.Equals(r.Name, role.Name)))
                    {
                        errors.Add(new DbValidationError("Role",
                            String.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, role.Name)));
                    }
                }
                if (errors.Any())
                {
                    return new DbEntityValidationResult(entityEntry, errors);
                }
            }
            return base.ValidateEntity(entityEntry, items);
        }
    }
    
    受保护的覆盖DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
    (词典项目)
    {
    if(entityEntry!=null&&entityEntry.State==EntityState.Added)
    {
    var errors=新列表();
    var user=entityEntry.Entity作为TUser;
    //检查用户名和电子邮件的唯一性
    如果(用户!=null)
    {
    if(Users.Any(u=>String.Equals(u.UserName,user.UserName)))
    {
    添加(新的DbValidationError(“用户”,
    格式(CultureInfo.CurrentCulture,IdentityResources.DuplicateUserName,user.UserName));
    }
    if(RequireUniqueEmail&&Users.Any(u=>String.Equals(u.Email,user.Email)))
    {
    添加(新的DbValidationError(“用户”,
    格式(CultureInfo.CurrentCulture、IdentityResources.DuplicateEmail、user.Email));
    }
    }
    其他的
    {
    var role=entityEntry.Entity作为TRole;
    //检查角色名称的唯一性
    if(role!=null&&Roles.Any(r=>String.Equals(r.Name,role.Name)))
    {
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            if (modelBuilder == null)
            {
                throw new ArgumentNullException("modelBuilder");
            }
    
            // Needed to ensure subclasses share the same table
            var user = modelBuilder.Entity<TUser>()
                .ToTable("AspNetUsers");
            user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
            user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
            user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
            user.Property(u => u.UserName)
                .IsRequired()
                .HasMaxLength(256)
                .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UserNameIndex") { IsUnique = true }));
    
            // CONSIDER: u.Email is Required if set on options?
            user.Property(u => u.Email).HasMaxLength(256);
    
            modelBuilder.Entity<TUserRole>()
                .HasKey(r => new { r.UserId, r.RoleId })
                .ToTable("AspNetUserRoles");
    
            modelBuilder.Entity<TUserLogin>()
                .HasKey(l => new { l.LoginProvider, l.ProviderKey, l.UserId })
                .ToTable("AspNetUserLogins");
    
            modelBuilder.Entity<TUserClaim>()
                .ToTable("AspNetUserClaims");
    
            var role = modelBuilder.Entity<TRole>()
                .ToTable("AspNetRoles");
            role.Property(r => r.Name)
                .IsRequired()
                .HasMaxLength(256)
                .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") { IsUnique = true }));
            role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
        }
    
    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
            IDictionary<object, object> items)
        {
            if (entityEntry != null && entityEntry.State == EntityState.Added)
            {
                var errors = new List<DbValidationError>();
                var user = entityEntry.Entity as TUser;
                //check for uniqueness of user name and email
                if (user != null)
                {
                    if (Users.Any(u => String.Equals(u.UserName, user.UserName)))
                    {
                        errors.Add(new DbValidationError("User",
                            String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, user.UserName)));
                    }
                    if (RequireUniqueEmail && Users.Any(u => String.Equals(u.Email, user.Email)))
                    {
                        errors.Add(new DbValidationError("User",
                            String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateEmail, user.Email)));
                    }
                }
                else
                {
                    var role = entityEntry.Entity as TRole;
                    //check for uniqueness of role name
                    if (role != null && Roles.Any(r => String.Equals(r.Name, role.Name)))
                    {
                        errors.Add(new DbValidationError("Role",
                            String.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, role.Name)));
                    }
                }
                if (errors.Any())
                {
                    return new DbEntityValidationResult(entityEntry, errors);
                }
            }
            return base.ValidateEntity(entityEntry, items);
        }
    }
    
    public class AppIdentityDbContext : IdentityDbContext<AppUser>
    {
    
    public AppIdentityDbContext()
        : base("IdentityContext", throwIfV1Schema: false)
    {
    }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder); // This needs to go before the other rules!
    
         *****[skipped some other code]*****
    
        // In order to support multiple user names 
        // I replaced unique index of UserNameIndex to non-unique
        modelBuilder
        .Entity<AppUser>()
        .Property(c => c.UserName)
        .HasColumnAnnotation(
            "Index", 
            new IndexAnnotation(
            new IndexAttribute("UserNameIndex")
            {
                IsUnique = false
            }));
    
        modelBuilder
            .Entity<AppUser>()
            .Property(c => c.Email)
            .IsRequired()
            .HasColumnAnnotation(
                "Index",
                new IndexAnnotation(new[]
                {
                    new IndexAttribute("EmailIndex") {IsUnique = true}
                }));
    }
    
    /// <summary>
    ///     Override 'ValidateEntity' to support multiple users with the same name
    /// </summary>
    /// <param name="entityEntry"></param>
    /// <param name="items"></param>
    /// <returns></returns>
    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
        IDictionary<object, object> items)
    {
        // call validate and check results 
        var result = base.ValidateEntity(entityEntry, items);
    
        if (result.ValidationErrors.Any(err => err.PropertyName.Equals("User")))
        {
            // Yes I know! Next code looks not good, because I rely on internal messages of Identity 2, but I should track here only error message instead of rewriting the whole IdentityDbContext
    
            var duplicateUserNameError = 
                result.ValidationErrors
                .FirstOrDefault(
                err =>  
                    Regex.IsMatch(
                        err.ErrorMessage,
                        @"Name\s+(.+)is\s+already\s+taken",
                        RegexOptions.IgnoreCase));
    
            if (null != duplicateUserNameError)
            {
                result.ValidationErrors.Remove(duplicateUserNameError);
            }
        }
    
        return result;
    }
    }
    
    public class AppUserValidator : IIdentityValidator<AppUser>
    {
    /// <summary>
    ///     Constructor
    /// </summary>
    /// <param name="manager"></param>
    public AppUserValidator(UserManager<AppUser> manager)
    {
        Manager = manager;
    }
    
    private UserManager<AppUser, string> Manager { get; set; }
    
    /// <summary>
    ///     Validates a user before saving
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public virtual async Task<IdentityResult> ValidateAsync(AppUser item)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }
    
        var errors = new List<string>();
    
        ValidateUserName(item, errors);
        await ValidateEmailAsync(item, errors);
    
        if (errors.Count > 0)
        {
            return IdentityResult.Failed(errors.ToArray());
        }
        return IdentityResult.Success;
    }
    
    private void ValidateUserName(AppUser user, List<string> errors)
    {
        if (string.IsNullOrWhiteSpace(user.UserName))
        {
            errors.Add("Name cannot be null or empty.");
        }
        else if (!Regex.IsMatch(user.UserName, @"^[A-Za-z0-9@_\.]+$"))
        {
            // If any characters are not letters or digits, its an illegal user name
            errors.Add(string.Format("User name {0} is invalid, can only contain letters or digits.", user.UserName));
        }
    }
    
    // make sure email is not empty, valid, and unique
    private async Task ValidateEmailAsync(AppUser user, List<string> errors)
    {
        var email = user.Email;
    
        if (string.IsNullOrWhiteSpace(email))
        {
            errors.Add(string.Format("{0} cannot be null or empty.", "Email"));
            return;
        }
        try
        {
            var m = new MailAddress(email);
        }
        catch (FormatException)
        {
            errors.Add(string.Format("Email '{0}' is invalid", email));
            return;
        }
        var owner = await Manager.FindByEmailAsync(email);
        if (owner != null && !owner.Id.Equals(user.Id))
        {
            errors.Add(string.Format(CultureInfo.CurrentCulture, "Email '{0}' is already taken.", email));
        }
    }
    }
    
    public class AppUserManager : UserManager<AppUser>
    {
    public AppUserManager(
        IUserStore<AppUser> store,
        IDataProtectionProvider dataProtectionProvider,
        IIdentityMessageService emailService)
        : base(store)
    {
    
        // Configure validation logic for usernames
        UserValidator = new AppUserValidator(this);
    
    public class AppSignInManager : SignInManager<AppUser, string>
    {
     ....
    public virtual async Task<SignInStatus> PasswordSignInViaEmailAsync(string userEmail, string password, bool isPersistent, bool shouldLockout)
    {
        var userManager = ((AppUserManager) UserManager);
        if (userManager == null)
        {
            return SignInStatus.Failure;
        }
    
        var user = await UserManager.FindByEmailAsync(userEmail);
        if (user == null)
        {
            return SignInStatus.Failure;
        }
    
        if (await UserManager.IsLockedOutAsync(user.Id))
        {
            return SignInStatus.LockedOut;
        }
    
        if (await UserManager.CheckPasswordAsync(user, password))
        {
            await UserManager.ResetAccessFailedCountAsync(user.Id);
            await SignInAsync(user, isPersistent, false);
            return SignInStatus.Success;
        }
    
        if (shouldLockout)
        {
            // If lockout is requested, increment access failed count which might lock out the user
            await UserManager.AccessFailedAsync(user.Id);
            if (await UserManager.IsLockedOutAsync(user.Id))
            {
                return SignInStatus.LockedOut;
            }
        }
        return SignInStatus.Failure;
    }
    
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Index(User model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        var result = 
            await signInManager.PasswordSignInViaEmailAsync(
                model.Email,
                model.Password, 
                model.StaySignedIn,
                true);
    
        var errorMessage = string.Empty;
        switch (result)
        {
            case SignInStatus.Success:
                if (IsLocalValidUrl(returnUrl))
                {
                    return Redirect(returnUrl);
                }
    
                return RedirectToAction("Index", "Home");
            case SignInStatus.Failure:
                errorMessage = Messages.LoginController_Index_AuthorizationError;
                break;
            case SignInStatus.LockedOut:
                errorMessage = Messages.LoginController_Index_LockoutError;
                break;
            case SignInStatus.RequiresVerification:
                throw new NotImplementedException();
        }
    
        ModelState.AddModelError(string.Empty, errorMessage);
        return View(model);
    }