【问题标题】:Disable User in ASPNET identity 2.0在 ASPNET 身份 2.0 中禁用用户
【发布时间】:2021-03-11 23:18:08
【问题描述】:

我正在寻找一种方法来禁用用户而不是从系统中删除他们,这是为了保持相关数据的数据完整性。但似乎 ASPNET 身份仅提供删除帐户。

有一个新的锁定功能,但似乎可以控制锁定以禁用用户,但只有在尝试一定次数的错误密码后才能锁定用户。

还有其他选择吗?

【问题讨论】:

    标签: asp.net asp.net-identity


    【解决方案1】:

    当您创建安装了身份位的站点时,您的站点将有一个名为“IdentityModels.cs”的文件。在这个文件中有一个名为 ApplicationUser 的类,它继承自 IdentityUser。

    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://devblogs.microsoft.com/aspnet/customizing-profile-information-in-asp-net-identity-in-vs-2013-templates/ to learn more.
    public class ApplicationUser : IdentityUser
    

    那里的cmets中有一个不错的链接,为方便点击here

    本教程准确地告诉您为您的用户添加自定义属性需要做什么。

    实际上,甚至不必费心看教程。

    1. 给ApplicationUser类添加一个属性,例如:

      公共布尔值?已启用 { 获取;放; }

    2. 在数据库的 AspNetUsers 表中添加同名列。

    3. 轰隆隆,就是这样!

    现在在您的 AccountController 中,您有一个注册操作,如下所示:

    public async Task<ActionResult> Register(RegisterViewModel model)
            {
                if (ModelState.IsValid)
                {
                    var user = new ApplicationUser { UserName = model.Email, Email = model.Email, IsEnabled = true };
                    var result = await UserManager.CreateAsync(user, model.Password);
                    if (result.Succeeded)
    

    我在创建 ApplicationUser 对象时添加了 IsEnabled = true。该值现在将保留在 AspNetUsers 表的新列中。

    然后,您需要通过覆盖 ApplicationSignInManager 中的 PasswordSignInAsync 来处理检查此值作为登录过程的一部分。

    我是这样做的:

    public override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
        {
            var user = UserManager.FindByEmailAsync(userName).Result;
    
            if ((user.IsEnabled.HasValue && !user.IsEnabled.Value) || !user.IsEnabled.HasValue)
            {
                return Task.FromResult<SignInStatus>(SignInStatus.LockedOut);
            }
    
            return base.PasswordSignInAsync(userName, password, rememberMe, shouldLockout);
        }
    

    您的里程可能会有所不同,您可能不想返回该 SignInStatus,但您明白了。

    【讨论】:

    • 是否需要检查用户是否存在以防止在 user.IsEnabled 上出现 NRE?我有以下内容:if (user!= null &amp;&amp; !user.IsEnabled) { return Task.FromResult&lt;SignInStatus&gt;(SignInStatus.LockedOut); }。我不检查 HasValue 的原因是因为我将 IsEnabled 设为必填字段
    • 这是一个更好的方法。因为我们正在覆盖PasswordSignInAsync。如果我们不这样做并且PasswordSignInAsync 返回Success。然后请求通过身份验证并且系统可以中断。因为可以写一些逻辑Request.IsAuthenticated。非常感谢:)
    • 是否建议改用 CanSignIn 方法?覆盖passwordsignin方法不检查其他登录方法?你可以在这里看到整个signinmanager github.com/aspnet/AspNetCore/blob/master/src/Identity/src/…
    • 您可以将 if 语句更改为 if (!user.IsEnabled.HasValue || !user.IsEnabled.Value) 或者只使用 bool 而不是 bool?。目前还不清楚将其设为可空有什么好处。
    【解决方案2】:

    User 的默认 LockoutEnabled 属性不是指示用户当前是否被锁定的属性。这是一个属性,指示一旦AccessFailedCount 达到MaxFailedAccessAttemptsBeforeLockout 值,用户是否应该被锁定。即使用户被锁定,它也只是在LockedoutEnddateUtc 属性的持续时间内禁止用户的临时措施。因此,要永久禁用或暂停用户帐户,您可能需要引入自己的标志属性。

    【讨论】:

      【解决方案3】:

      您无需创建自定义属性。诀窍是设置 身份用户上的LockoutEnabled 属性并将LockoutoutEndDateUtc 设置为您的代码中的未来日期以锁定用户。然后,调用 UserManager.IsLockedOutAsync(user.Id) 将返回 false。

      LockoutEnabledLockoutoutEndDateUtc 都必须满足真实和未来日期的条件才能锁定用户。例如,如果LockoutoutEndDateUtc 的值为2014-01-01 00:00:00.000 并且LockoutEnabledtrue,则调用UserManager.IsLockedOutAsync(user.Id) 仍将返回true。我可以理解为什么微软这样设计它,这样你就可以设置一个用户被锁定的时间跨度。

      但是,我认为如果LockoutEnabledtrue,那么如果LockoutoutEndDateUtc 为NULL 或未来日期,用户应该被锁定。这样您就不必担心在代码中设置两个属性(LockoutoutEndDateUtc 默认为 NULL)。您可以将LockoutEnabled 设置为true,如果LockoutoutEndDateUtcNULL,则用户将被无限期锁定。

      【讨论】:

      • 它得到了很多人的支持,但这个答案是错误的,因为正如其他人所说,您误解了 LockoutEnabled 属性。它指示用户是否“可以被锁定”。应该有一个内置属性,简称为“LockedOut”或类似的。更多信息:jamessturtevant.com/posts/ASPNET-Identity-Lockout
      • LockoutEnabled 为真,而未来的 LockoutEndDateUtc 意味着 .IsLockedOutAsync 为真,而不是假。 (第 1 页。)如果 2014-01-01 是过去的(就像最初发布的那样),.IsLockedOutAsync 将“仍然”返回 FALSE,而不是 true。第三个ppg是一个模糊的概念。这到底是怎么得到赞成的?
      • 是的,你是对的,2014-01-01 是错误的——它应该是一个未来的日期,比如 DateTime.MaxValue。如果 LockoutEnabled 配置为 true,则当用户超过配置的 MaxFailedAccessAttempts 次数时,LockoutEndate 字段将从当前时间设置为 DefaultLockoutTimeSpan 配置的任何时间。正如你所提到的,我的第三个 ppg 有点模糊。简而言之,我同意应该有一个 bool 字段来标记一个人是否被锁定,并且不必手动编码以将未来日期设置到 LockoutEnd 字段来完成同样的事情。
      【解决方案4】:

      您需要将自己的标志引入自定义 IdentityUser 派生类,并实施/实施您自己的有关启用/禁用的逻辑,并在禁用时阻止用户登录。

      【讨论】:

      • 那么帐户锁定怎么样。我粗略看了一下,看起来我不应该用它来做这个目的,对吧?
      • 在 v2 中,由于暴力破解密码,支持帐户锁定。
      【解决方案5】:

      我实际上所做的一切:

          var lockoutEndDate = new DateTime(2999,01,01);
          UserManager.SetLockoutEnabled(userId,true);
          UserManager.SetLockoutEndDate(userId, lockoutEndDate);
      

      这基本上是为了启用锁定(如果您默认情况下没有这样做,然后将锁定结束日期设置为某个遥远的值。

      【讨论】:

      • 这行得通,而且可能是我要采用的方法,但我们必须做这样的事情似乎很愚蠢,为什么不能只有一个锁定标志?!? !
      【解决方案6】:

      Ozz 是正确的,但是建议您查看基类,看看您是否可以找到一个检查所有符号角度的方法 - 我认为它可能是 CanSignIn?

      现在 MS 是开源的,你可以看到它们的实现:

      https://github.com/aspnet/AspNetCore/blob/master/src/Identity/src/Identity/SignInManager.cs

      (网址已更改为:

      https://github.com/aspnet/AspNetCore/blob/master/src/Identity/Core/src/SignInManager.cs)

          public class CustomSignInManager : SignInManager<ApplicationUser>  
      {
          public CustomSignInManager(UserManager<ApplicationUser> userManager,
              IHttpContextAccessor contextAccessor,
              IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
              IOptions<IdentityOptions> optionsAccessor,
              ILogger<SignInManager<ApplicationUser>> logger,
              IAuthenticationSchemeProvider schemes) : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes)
          {
      
          }
      
      
          public override async Task<bool> CanSignInAsync(ApplicationUser user)
          {
              if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user)))
              {
                  Logger.LogWarning(0, "User {userId} cannot sign in without a confirmed email.", await UserManager.GetUserIdAsync(user));
                  return false;
              }
              if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user)))
              {
                  Logger.LogWarning(1, "User {userId} cannot sign in without a confirmed phone number.", await UserManager.GetUserIdAsync(user));
                  return false;
              }
      
              if (UserManager.FindByIdAsync(user.Id).Result.IsEnabled == false)
              {
                  Logger.LogWarning(1, "User {userId} cannot sign because it's currently disabled", await UserManager.GetUserIdAsync(user));
                  return false;
              }
      
              return true;
          }
      }
      

      还可以考虑覆盖 PreSignInCheck,它也调用 CanSignIn

      protected virtual async Task<SignInResult> PreSignInCheck(TUser user)
              {
                  if (!await CanSignInAsync(user))
                  {
                      return SignInResult.NotAllowed;
                  }
                  if (await IsLockedOut(user))
                  {
                      return await LockedOut(user);
                  }
                  return null;
              }
      

      【讨论】:

      • Identity v3 新增了options.SignIn.RequireConfirmedAccount & IUserConfirmation&lt;TUser&gt;,所以你不需要再更换SignInManager了。
      【解决方案7】:

      您可以使用这些类... ASP.NET Identity 的干净实现... 这是我自己的代码。 int 在这里是主键,如果你想要不同的主键类型,你可以改变它。

      IdentityConfig.cs

      public class ApplicationUserManager : UserManager<ApplicationUser, int>
      {
          public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
              : base(store)
          {
          }
          public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
          {
              var manager = new ApplicationUserManager(new ApplicationUserStore(context.Get<ApplicationContext>()));
              manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
              {
                  AllowOnlyAlphanumericUserNames = false,
                  RequireUniqueEmail = true
              };
              manager.PasswordValidator = new PasswordValidator
              {
                  RequiredLength = 6,
                  RequireNonLetterOrDigit = true,
                  RequireDigit = true,
                  RequireLowercase = true,
                  RequireUppercase = true,
              };
              manager.UserLockoutEnabledByDefault = false;
              var dataProtectionProvider = options.DataProtectionProvider;
              if (dataProtectionProvider != null)
              {
                  manager.UserTokenProvider =
                      new DataProtectorTokenProvider<ApplicationUser, int>(
                          dataProtectionProvider.Create("ASP.NET Identity"));
              }
              return manager;
          }
      }
      public class ApplicationSignInManager : SignInManager<ApplicationUser, int>
      {
          public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) :
              base(userManager, authenticationManager) { }
          public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
          {
              return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
          }
          public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
          {
              return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
          }
      }
      public class ApplicationRoleManager : RoleManager<ApplicationRole, int>
      {
          public ApplicationRoleManager(IRoleStore<ApplicationRole, int> store)
              : base(store)
          {
          }
      }
      public class ApplicationRoleStore : RoleStore<ApplicationRole, int, ApplicationUserRole>
      {
          public ApplicationRoleStore(ApplicationContext db)
              : base(db)
          {
          }
      }
      public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, int,
      ApplicationLogin, ApplicationUserRole, ApplicationClaim>
      {
          public ApplicationUserStore(ApplicationContext db)
              : base(db)
          {
          }
      }
      

      IdentityModel.cs

      public class ApplicationUser : IdentityUser<int, ApplicationLogin, ApplicationUserRole, ApplicationClaim>
      {   
          //your property 
          //flag for users state (active, deactive or enabled, disabled)
          //set it false to disable users
          public bool IsActive { get; set; }
          public ApplicationUser()
          {
          }
          public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
          {
              var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
              return userIdentity;
          }
      }
      public class ApplicationUserRole : IdentityUserRole<int>
      {
      }
      public class ApplicationLogin : IdentityUserLogin<int>
      {
          public virtual ApplicationUser User { get; set; }
      }
      public class ApplicationClaim : IdentityUserClaim<int>
      {
          public virtual ApplicationUser User { get; set; }
      }
      public class ApplicationRole : IdentityRole<int, ApplicationUserRole>
      {
          public ApplicationRole()
          {
          }
      }
      public class ApplicationContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationLogin, ApplicationUserRole, ApplicationClaim>
      {
          //web config connectionStringName DefaultConnection change it if required
          public ApplicationContext()
              : base("DefaultConnection")
          {
              Database.SetInitializer<ApplicationContext>(new CreateDatabaseIfNotExists<ApplicationContext>());
          }
          public static ApplicationContext Create()
          {
              return new ApplicationContext();
          }
          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
              base.OnModelCreating(modelBuilder);
              modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
              modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
              modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
          }
      }  
      

      【讨论】:

        【解决方案8】:

        我赞成 Watson,因为 SignInManager 中有另一个公共方法接受 TUser 用户而不是字符串 userName。接受的答案仅建议使用用户名签名覆盖该方法。两者都应该被覆盖,否则有一种方法可以让禁用的用户登录。以下是基本实现中的两个方法:

        public virtual async Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)
        {
          var user = await UserManager.FindByNameAsync(userName);
          if (user == null)
          {
            return SignInResult.Failed;
          }
        
          return await PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure);
        }
        
        public virtual async Task<SignInResult> PasswordSignInAsync(User user, string password, bool isPersistent, bool lockoutOnFailure)
        {
          if (user == null)
          {
            throw new ArgumentNullException(nameof(user));
          }
        
          var attempt = await CheckPasswordSignInAsync(user, password, lockoutOnFailure);
          return attempt.Succeeded
              ? await SignInOrTwoFactorAsync(user, isPersistent)
              : attempt;
        }
        

        覆盖 CanSignIn 对我来说似乎是一个更好的解决方案,因为它由 PreSignInCheck 调用,在 CheckPasswordSignInAsync 中调用。据我所知,覆盖 CanSignIn 应该涵盖所有场景。这是一个可以使用的简单实现:

        public override async Task<bool> CanSignInAsync(User user)
        {
          var canSignIn = user.IsEnabled;
        
          if (canSignIn) { 
            canSignIn = await base.CanSignInAsync(user);
          }
          return canSignIn;
        }
        

        【讨论】:

          【解决方案9】:

          在 asp.net Core Identity v3 中,添加了一种防止用户登录的新方法。以前您可以要求帐户具有确认的电子邮件地址或电话号码,现在您可以指定.RequireConfirmedAccountIUserConfirmation&lt;&gt; 服务的默认实现与要求确认电子邮件地址的行为相同,提供您自己的服务来定义确认的含义。

              public class User : IdentityUser<string>{
                  public bool IsEnabled { get; set; }
              }
          
              public class UserConfirmation : IUserConfirmation<User>
              {
                  public Task<bool> IsConfirmedAsync(UserManager<User> manager, User user) => 
                      Task.FromResult(user.IsEnabled);
              }
          
              services.AddScoped<IUserConfirmation<User>, UserConfirmation>();
              services.AddIdentity<User, IdentityRole>(options => { 
                  options.SignIn.RequireConfirmedAccount = true;
              } );
          
          

          【讨论】:

            【解决方案10】:

            您需要实现自己的UserStore 来移除身份。

            this 也可能对您有所帮助。

            【讨论】:

              猜你喜欢
              • 2017-11-03
              • 1970-01-01
              • 2018-07-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-24
              相关资源
              最近更新 更多