我正在寻找一种方法来禁用用户而不是从系统中删除它们,这是为了保持相关数据的数据完整性。但似乎ASPNET身份只提供删除帐户。
有一个新的锁定功能,但似乎可以控制锁定以禁用用户,但只有在尝试了一定数量的错误密码后才能锁定用户。
还有其他选择吗?
答案 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。
LockoutEnabled
和LockoutoutEndDateUtc
都必须符合锁定用户的真实和未来日期的标准。例如,如果LockoutoutEndDateUt
c值为2014-01-01 00:00:00.000
且LockoutEnabled
为true
,则调用UserManager.IsLockedOutAsync(user.Id)
仍会返回true
。我可以看到为什么Microsoft以这种方式设计它,以便您可以设置用户被锁定的时间跨度。
但是,我认为如果LockoutEnabled
为true
,那么如果LockoutoutEndDateUtc
为NULL或未来日期,则应锁定用户。这样您就不必担心代码中有关设置两个属性的问题(LockoutoutEndDateUtc
默认为NULL
)。您可以将LockoutEnabled
设置为true
,如果LockoutoutEndDateUtc
为NULL
,则用户将被无限期锁定。
答案 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>();
}
}
答案 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中,添加了一种防止用户登录的新方法。以前,您可能需要一个帐户具有确认的电子邮件地址或电话号码,现在您可以指定.RequireConfirmedAccount
。 IUserConfirmation<>
服务的默认实现与要求确认电子邮件地址的行为相同,请提供您自己的服务来定义确认的含义。
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)
答案 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;
}