ASP.NET Identiteitswijzigingswachtwoord

Ik heb de mogelijkheid nodig om het wachtwoord voor de gebruiker door de beheerder te wijzigen. De beheerder mag dus geen huidig ​​wachtwoord van de gebruiker invoeren, hij moet de mogelijkheid hebben om een ​​nieuw wachtwoord in te stellen. Ik kijk naar de methode ChangePasswordAsync, maar voor deze methode moet een oud wachtwoord worden ingevoerd. Deze methode is dus niet geschikt voor deze taak. Daarom heb ik het op de volgende manier gemaakt:

   [HttpPost]
    public async Task<ActionResult> ChangePassword(ViewModels.Admin.ChangePasswordViewModel model)
    {
        var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = await userManager.RemovePasswordAsync(model.UserId);
        if (result.Succeeded)
        {
            result = await userManager.AddPasswordAsync(model.UserId, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("UserList");
            }
            else
            {
                ModelState.AddModelError("", result.Errors.FirstOrDefault());
            }
        }
        else
        {
            ModelState.AddModelError("", result.Errors.FirstOrDefault());
        }
        return View(model);
    }

het werkt, maar theoretisch kunnen we een fout ontvangen op de AddPasswordAsync-methode. Het oude wachtwoord wordt dus verwijderd, maar het nieuwe is niet ingesteld. Het is niet goed. Een manier om het in “één transactie” te doen?
ps. Ik heb de ResetPasswordAsync-methode met reset-token gezien, het lijkt veiliger (omdat het geen onstabiele situatie met de gebruiker kan zijn), maar in ieder geval doet het dat met 2 acties.


Antwoord 1, autoriteit 100%

EDIT: ik weet dat de OP om een ​​antwoord vroeg dat de taak in één transactie uitvoert, maar ik denk dat de code nuttig is voor mensen.

Alle antwoorden gebruiken de PasswordHasher rechtstreeks, wat geen goed idee is, omdat je wat ingebakken functionaliteit verliest (validatie, enz.).

Een alternatief (en ik neem aan dat dit de aanbevolen aanpak is) is om een ​​wachtwoordhersteltoken te maken en dat vervolgens te gebruiken om het wachtwoord te wijzigen. Voorbeeld:

var user = await UserManager.FindByIdAsync(id);
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, "[email protected]");

Antwoord 2, autoriteit 54%

Deze methode werkte voor mij:

public async Task<IHttpActionResult> changePassword(UsercredentialsModel usermodel)
{
  ApplicationUser user = await AppUserManager.FindByIdAsync(usermodel.Id);
  if (user == null)
  {
    return NotFound();
  }
  user.PasswordHash = AppUserManager.PasswordHasher.HashPassword(usermodel.Password);
  var result = await AppUserManager.UpdateAsync(user);
  if (!result.Succeeded)
  {
    //throw exception......
  }
  return Ok();
}

Antwoord 3, autoriteit 36%

ApplicationUserManageris de klasse die wordt gegenereerd door de ASP.NET-sjabloon.

Dat betekent dat je het kunt bewerken en alle functionaliteit kunt toevoegen die het nog niet heeft. De klasse UserManager heeft een beschermde eigenschap met de naam Storedie een verwijzing opslaat naar de klasse UserStore(of een subklasse ervan, afhankelijk van hoe u uw ASP.NET-identiteit hebt geconfigureerd of u gebruikt aangepaste gebruikersopslagimplementaties, dat wil zeggen als u een andere database-engine gebruikt, zoals MySQL).

public class AplicationUserManager : UserManager<....> 
{
    public async Task<IdentityResult> ChangePasswordAsync(TKey userId, string newPassword) 
    {
        var store = this.Store as IUserPasswordStore;
        if(store==null) 
        {
            var errors = new string[] 
            { 
                "Current UserStore doesn't implement IUserPasswordStore"
            };
            return Task.FromResult<IdentityResult>(new IdentityResult(errors) { Succeeded = false });
        }
        if(PasswordValidator != null)
        {
            var passwordResult = await PasswordValidator.ValidateAsync(password);
            if(!password.Result.Success)
                return passwordResult;
        }
        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
        await store.SetPasswordHashAsync(userId, newPasswordHash);
        return Task.FromResult<IdentityResult>(IdentityResult.Success);
    }
}

De UserManageris niets anders dan een wrapper voor de onderliggende UserStore. Bekijk de interfacedocumentatie van IUserPasswordStoreop MSDNop beschikbare methoden.

Bewerken:
De PasswordHasheris ook een openbare eigenschap van de klasse UserManager, zie interfacedefinitie hier.

Bewerken 2:
Omdat sommige mensen naïefgeloven dat je op deze manier geen wachtwoordvalidatie kunt uitvoeren, heb ik het bijgewerkt. De eigenschap PasswordValidatoris ook een eigenschap van UserManageren het is zo simpel als het toevoegen van 2 regels code om ook wachtwoordvalidatie toe te voegen (wat echter geen vereiste was voor de oorspronkelijke vraag ).


Antwoord 4, autoriteit 11%

In .net core 3.0

var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, password);

Antwoord 5, autoriteit 6%

Dit is slechts een verfijning van het antwoord van @Tseng. (Ik moest het aanpassen om het te laten werken).

public class AppUserManager : UserManager<AppUser, int>
{
    .
    // standard methods...
    .
    public async Task<IdentityResult> ChangePasswordAsync(AppUser user, string newPassword)
    {
        if (user == null)
            throw new ArgumentNullException(nameof(user));
        var store = this.Store as IUserPasswordStore<AppUser, int>;
        if (store == null)
        {
            var errors = new string[] { "Current UserStore doesn't implement IUserPasswordStore" };
            return IdentityResult.Failed(errors);
        }
        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
        await store.SetPasswordHashAsync(user, newPasswordHash);
        await store.UpdateAsync(user);
        return IdentityResult.Success;
    }
}

Opmerking: dit is specifiek van toepassing op een aangepaste setup die intgebruikt als primaire sleutels voor gebruikers en rollen. Ik geloof dat het gewoon een kwestie is van het verwijderen van de <AppUser, int>typeargs om het te laten werken met de standaard ASP.NET Identity-configuratie.


Antwoord 6, autoriteit 2%

Ik denk dat de oplossing veel eenvoudiger is

  1. Genereer het wachtwoordToken,
  2. Reset het wachtwoord met de gegenereerde token…
public async Task<IdentityResult> ResetPasswordAsync(ApplicationUser user, string password)
{
    string token = await userManager.GeneratePasswordResetTokenAsync(user);
    return await userManager.ResetPasswordAsync(user, token, password);
}

Antwoord 7, autoriteit 2%

public async Task<IActionResult> ChangePassword(ChangePwdViewModel usermodel)
        {           
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            var user = await _userManager.FindByIdAsync(userId);            
            var result = await _userManager.ChangePasswordAsync(user, usermodel.oldPassword, usermodel.newPassword);
            if (!result.Succeeded)
            {
                //throw exception......
            }
            return Ok();
        }
public class ChangePwdViewModel
    {  
        [DataType(DataType.Password), Required(ErrorMessage ="Old Password Required")]
        public string oldPassword { get; set; }
        [DataType(DataType.Password), Required(ErrorMessage ="New Password Required")]
        public string newPassword { get; set; }
    }

Opmerking: hier haal ik de UserId op van de huidige ingelogde gebruiker.


Antwoord 8, autoriteit 2%

Als u het huidige wachtwoord van de gebruiker niet heeft en toch het wachtwoord wilt wijzigen. Wat u in plaats daarvan kunt doen, is eerst het gebruikerswachtwoord verwijderen en vervolgens het nieuwe wachtwoord toevoegen. Op deze manier kunt u het wachtwoord van de gebruiker wijzigen zonder dat u het huidige wachtwoord van die gebruiker nodig heeft.

await UserManager.RemovePasswordAsync(user);
await UserManager.AddPasswordAsync(user, model.Password);

Antwoord 9

Voor gebruikers van ASP.NET Core 3.1is dit een gemoderniseerde versie van de uitstekende antwoorden van @Tseng en @BCA.

PasswordValidatoris niet langer een eigenschap op UserManager– in plaats daarvan is de eigenschap een IList PasswordValidators. Verder heeft Identity nu een protected UpdatePasswordHash-methode die het wachtwoord voor u verandert zonder dat u rechtstreeks toegang hoeft te krijgen tot de UserStore, waardoor het niet meer nodig is om het wachtwoord handmatig te hashen en op te slaan.

UserManagerheeft ook een openbare eigenschap, bool SupportsUserPassword, die de noodzaak vervangt om te testen of StoreIUserPasswordStore(intern is dit precies wat UserManagerdoet in de SupportsUserPasswordgetter).

Aangezien UpdatePasswordHashprotectedis, moet u nog steeds de basis UserManageruitbreiden. Zijn handtekening is:

protected Task<IdentityResult> UpdatePasswordHash(TUser user, string newPassword, bool validatePassword)

waarbij validatePasswordstaat voor het al dan niet uitvoeren van wachtwoordvalidatie. Dit is nietstandaard true, helaas, dus het moet worden opgegeven. De implementatie ziet er als volgt uit:

public async Task<IdentityResult> ChangePasswordAsync(ApplicationUser user, string newPassword)
{
    if (!SupportsUserPassword)
    {
        return IdentityResult.Failed(new IdentityError
        {
            Description = "Current UserStore doesn't implement IUserPasswordStore"
        });
    }
    var result = await UpdatePasswordHash(user, newPassword, true);
    if (result.Succeeded)
        await UpdateAsync(user);
    return result;
}

Zoals voorheen is de eerste taak ervoor te zorgen dat de huidige UserStorewachtwoorden ondersteunt.

Bel vervolgens gewoon UpdatePasswordHashaan met de ApplicationUser, het nieuwe wachtwoord en trueom het wachtwoord van die gebruiker met validatie bij te werken. Als de update is gelukt, moet u de gebruiker nog steeds opslaan, dus bel UpdateAsync.


Antwoord 10

public async Task<ActionResult> ResetUserPassword(string id, string Password)
{
    //     Find User
    var user = await context.Users.Where(x => x.Id == id).SingleOrDefaultAsync();
    if (user == null)
    {
        return RedirectToAction("UserList");
    }
    await UserManager.RemovePasswordAsync(id);
    //     Add a user password only if one does not already exist
    await UserManager.AddPasswordAsync(id, Password);
    return RedirectToAction("UserDetail", new { id = id });
}

Antwoord 11

Ja, je hebt gelijk. ResetPassword via token heeft de voorkeur.
Enige tijd geleden heb ik een complete wrapper gemaakt over .NET Identity en code is te vinden hier. Het kan nuttig voor je zijn. Je kunt nuget ook hiervinden. Ik heb de bibliotheek ook uitgelegd in een blog hier . Deze wrapper kan gemakkelijk als nuget worden geconsumeerd en maakt tijdens de installatie alle vereiste configuraties.

Other episodes