Ich suche nach einer Möglichkeit, den Benutzer zu deaktivieren, anstatt sie aus dem System zu löschen. Dies ist, um die Datenintegrität der zugehörigen Daten zu erhalten. Scheinbar bietet die ASPNET-Identität jedoch nur die Option "Konto löschen".
Es gibt eine neue Sperrfunktion, aber es scheint, dass die Sperrung gesteuert werden kann, um den Benutzer zu deaktivieren. Der Benutzer kann jedoch nur nach einer bestimmten Anzahl falscher Kennwortversuche gesperrt werden.
Irgendwelche anderen Möglichkeiten?
Wenn Sie eine Site mit den installierten Identitätsbits erstellen, verfügt Ihre Site über eine Datei mit dem Namen "IdentityModels.cs". In dieser Datei befindet sich eine Klasse mit dem Namen ApplicationUser, die von IdentityUser erbt.
// 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
Es gibt einen schönen Link in den Kommentaren dort. Klicken Sie einfach auf hier
In diesem Lernprogramm erfahren Sie genau, was Sie tun müssen, um benutzerdefinierte Eigenschaften für Ihren Benutzer hinzuzufügen.
Und schauen Sie sich das Tutorial gar nicht erst an.
1) Fügen Sie der ApplicationUser-Klasse eine Eigenschaft hinzu, z.
public bool? IsEnabled { get; set; }
2) Fügen Sie in der AspNetUsers-Tabelle in Ihrer Datenbank eine Spalte mit demselben Namen hinzu.
3) boom, das ist es!
Jetzt haben Sie in Ihrem AccountController eine Registrierungsaktion wie folgt:
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)
Ich habe bei der Erstellung des ApplicationUser-Objekts IsEnabled = true hinzugefügt. Der Wert wird nun in Ihrer neuen Spalte in der AspNetUsers-Tabelle beibehalten.
Sie müssen dann im Rahmen des Anmeldeprozesses nach diesem Wert suchen, indem Sie PasswordSignInAsync in ApplicationSignInManager überschreiben.
Ich habe es wie folgt gemacht:
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);
}
Ihre Laufleistung kann variieren und Sie möchten den SignInStatus möglicherweise nicht zurückgeben, aber Sie haben die Idee.
Sie müssen keine benutzerdefinierte Eigenschaft erstellen. Der Trick besteht darin, die LockoutEnabled
-Eigenschaft für den Identitätsbenutzer festzulegen UND die LockoutoutEndDateUtc
auf ein zukünftiges Datum aus Ihrem Code zu setzen, um einen Benutzer zu sperren. Beim Aufruf von UserManager.IsLockedOutAsync(user.Id
) wird dann false zurückgegeben.
Sowohl LockoutEnabled
als auch LockoutoutEndDateUtc
müssen die Kriterien "wahres" und "zukünftiges Datum" erfüllen, um einen Benutzer zu sperren. Wenn der Wert für LockoutoutEndDateUt
c beispielsweise 2014-01-01 00:00:00.000
ist und LockoutEnabled
true
ist, wird beim Aufruf vonUserManager.IsLockedOutAsync(user.Id)
noch true
zurückgegeben. Ich kann sehen, warum Microsoft es so entworfen hat, damit Sie eine Zeitspanne festlegen können, wie lange ein Benutzer gesperrt ist.
Ich würde jedoch argumentieren, dass es sein sollte, wenn LockoutEnabled
true
ist, und der Benutzer sollte gesperrt werden, wenn LockoutoutEndDateUtc
NULL OR eines zukünftigen Datums ist. Auf diese Weise müssen Sie sich in Ihrem Code keine Gedanken über die Einstellung von zwei Eigenschaften machen (LockoutoutEndDateUtc
ist standardmäßig NULL
). Sie können einfach LockoutEnabled
auf true
setzen, und wenn LockoutoutEndDateUtc
NULL
ist, ist der Benutzer auf unbestimmte Zeit gesperrt.
Die standardmäßige LockoutEnabled
-Eigenschaft für ein User
ist nicht die Eigenschaft, die angibt, ob ein Benutzer derzeit gesperrt ist oder nicht. Diese Eigenschaft gibt an, ob der Benutzer gesperrt werden soll oder nicht, sobald AccessFailedCount
den MaxFailedAccessAttemptsBeforeLockout
-Wert erreicht. Selbst wenn der Benutzer gesperrt ist, ist dies nur eine vorübergehende Maßnahme, um den Benutzer für die Dauer der LockedoutEnddateUtc
-Eigenschaft zu sperren. Um ein Benutzerkonto dauerhaft zu deaktivieren oder zu sperren, möchten Sie möglicherweise Ihre eigene Flag-Eigenschaft einfügen.
Sie müssen Ihr eigenes Flag in eine benutzerdefinierte, von IdentityUser abgeleitete Klasse einführen und Ihre eigene Logik bezüglich Aktivieren/Deaktivieren implementieren/durchsetzen und verhindern, dass sich der Benutzer bei Deaktivierung anmeldet.
Sie können diese Klassen verwenden ... Eine saubere Implementierung von ASP.NET-Identität ..... Es ist mein eigener Code. int ist hier für Primärschlüssel, wenn Sie einen anderen Typ für Primärschlüssel wünschen, den Sie ändern können.
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>();
}
}
Das alles, was ich tatsächlich gemacht habe:
var lockoutEndDate = new DateTime(2999,01,01);
UserManager.SetLockoutEnabled(userId,true);
UserManager.SetLockoutEndDate(userId, lockoutEndDate);
Dies ist im Wesentlichen das Aktivieren der Sperrung (wenn Sie dies nicht bereits standardmäßig tun und dann das Enddatum der Sperrung auf einen entfernten Wert setzen).
Ich habe Watson auf den neuesten Stand gebracht, da es in SignInManager eine andere öffentliche Methode gibt, die TUser-Benutzer anstelle der Zeichenfolge userName akzeptiert. Die akzeptierte Antwort schlägt nur vor, die Methode mit der Signatur des Benutzernamens zu überschreiben. Beides sollte wirklich außer Kraft gesetzt werden, da es sonst eine Möglichkeit gibt, einen behinderten Benutzer anzumelden. Hier sind die beiden Methoden in der Basisimplementierung:
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;
}
Das Überschreiben von CanSignIn scheint mir eine bessere Lösung zu sein, da es von PreSignInCheck aufgerufen wird, das in CheckPasswordSignInAsync aufgerufen wird. Nach dem, was ich aus der Quelle ersehen kann, sollte das Überschreiben von CanSignIn alle Szenarien abdecken. Hier ist eine einfache Implementierung, die verwendet werden könnte:
public override async Task<bool> CanSignInAsync(User user)
{
var canSignIn = user.IsEnabled;
if (canSignIn) {
canSignIn = await base.CanSignInAsync(user);
}
return canSignIn;
}
Ozz ist korrekt, es kann jedoch ratsam sein, sich die Basisklasse anzusehen und herauszufinden, ob Sie eine Methode finden können, die für alle Vorzeichenwinkel geprüft wird. Ich denke, es könnte CanSignIn sein.
Nun, da MS Open Source ist, können Sie deren Implementierung sehen:
https://github.com/aspnet/AspNetCore/blob/master/src/Identity/src/Identity/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;
}
}