在ASPNET标识2.0中禁用用户

时间:2014-03-26 05:19:18

标签: asp.net asp.net-identity

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

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

还有其他选择吗?

10 个答案:

答案 0 :(得分:50)

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

// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser

评论中有一个很好的链接,方便点击here

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

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

1)向ApplicationUser类添加一个属性,例如:

public bool? IsEnabled { get; set; }

2)在数据库的AspNetUsers表中添加一个名称相同的列。

3)热潮,就是这样!

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

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,但您明白了。

答案 1 :(得分:19)

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

LockoutEnabledLockoutoutEndDateUtc都必须符合锁定用户的真实和未来日期的标准。例如,如果LockoutoutEndDateUt c值为2014-01-01 00:00:00.000LockoutEnabledtrue,则调用UserManager.IsLockedOutAsync(user.Id)仍会返回true。我可以看到为什么Microsoft以这种方式设计它,以便您可以设置用户被锁定的时间跨度。

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

答案 2 :(得分:17)

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

答案 3 :(得分:9)

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

答案 4 :(得分:3)

您可以使用这些类...... ASP.NET身份的干净实现...... 这是我自己的代码。如果您想要更改主键的不同类型,则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>();
    }
}  

For Example Code

答案 5 :(得分:2)

这就是我所做的一切:

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

这基本上是为了启用锁定(如果你没有默认执行此操作,然后将锁定结束日期设置为某个远程值。

答案 6 :(得分:2)

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;
        }

答案 7 :(得分:1)

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

    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;
    } );

答案 8 :(得分:0)

您需要实现自己的UserStore以删除身份。

this也可能对您有所帮助。

答案 9 :(得分:0)

我赞成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;
}