ASP.NET mot de passe de changement D'identité
j'ai besoin de la capacité de changer le mot de passe pour l'utilisateur par administrateur. Ainsi, l'administrateur ne devrait pas entrer un mot de passe d'utilisateur courant, il devrait avoir la capacité de définir un nouveau mot de passe. Je regarde la méthode ChangePasswordAsync, mais cette méthode nécessite d'entrer l'ancien mot de passe. Cette méthode ne convient donc pas à cette tâche. Donc, j'ai fait de la façon suivante:
[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);
}
cela fonctionne, mais théoriquement nous pouvons recevoir une erreur sur la méthode AddPasswordAsync. Ainsi, l'ancien mot de passe sera supprimé mais Nouveau n'est pas définir. Il n'est pas bon. Un moyen de le faire en"une transaction"? PS. J'ai vu la méthode ResetPasswordAsync avec le token reset, semble, c'est plus sûr (parce que ne peut pas être une situation instable avec l'utilisateur) mais dans tous les cas, il le fait par 2 actions.
8 réponses
ApplicationUserManager
est la classe générée par ASP.NET modèle.
ce qui signifie que vous pouvez l'éditer et ajouter n'importe quelle fonctionnalité qu'il n'a pas encore. La classe UserManager a une propriété protégée nommée Store
qui stocke une référence à l' UserStore
classe (ou toute sous-classe de celle - ci, selon la façon dont vous avez configuré votre ASP.NET identité ou si vous utilisez des implémentations custom user store, c'est-à-dire si vous utilisez un moteur de base de données différent comme 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);
}
}
UserManager
n'est rien d'autre qu'un wrapper de la sous -UserStore
. Découvrez IUserPasswordStore
documentation de l'interface à MSDN sur les Méthodes disponibles.
Edit:
PasswordHasher
est aussi un bien public de la UserManager
classe, voir définition de l'interface ici.
Edit 2:
Depuis que certaines personnes naïvement croyez, vous ne pouvez pas faire la validation de mot de passe de cette façon, je l'ai mis à jour. PasswordHasher
la propriété est aussi un UserManager
et c'est aussi simple que d'ajouter 2 lignes de code pour ajouter la validation du mot de passe aussi (ce qui n'était pas une condition de la question originale).
Cette méthode a fonctionné pour moi:
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();
}
EDIT: je sais que L'OP a demandé une réponse qui exécute la tâche en une transaction mais je pense que le code est utile aux gens.
toutes les réponses utilisent le mot de passe directement ce qui n'est pas une bonne idée car vous perdrez quelques fonctionnalités (validation etc).
Une alternative (et je suppose que l'approche recommandée est de créer une réinitialisation de mot de passe et ensuite l'utiliser pour changer le mot de passe. Exemple:
var user = await UserManager.FindByIdAsync(id);
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
C'est juste un raffinement sur la réponse fournie par @Tseng. (J'ai dû ruser pour le faire fonctionner).
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;
}
}
Note: Ceci s'applique spécifiquement à une configuration modifiée qui utilise int
comme les clés primaires pour les utilisateurs et les rôles. Je pense qu'il s'agirait simplement de supprimer les <AppUser, int>
tapez args pour qu'il fonctionne avec la valeur par défaut ASP.NET configuration de L'identité.
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; }
}
Note: ici UserId je récupère de L'utilisateur connecté courant.
public async Task<ActionResult> ChangePassword(ResetPasswordViewModel CP)
{
ApplicationDbContext context = new ApplicationDbContext();
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
var user = await UserManager.FindAsync(User.Identity.Name, CP.CurrentPassword);
if (!UserManager.CheckPassword(user, CP.CurrentPassword))
{
ViewBag.notification = "Incorrect password.";
return View("~/Views/User/settings.cshtml");
}
else
{
if (CP.Password != CP.ConfirmPassword)
{
ViewBag.notification = "try again";
return View("~/Views/User/settings.cshtml");
}
else
{
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(CP.Password);
await store.SetPasswordHashAsync(user, hashedNewPassword);
await store.UpdateAsync(user);
ViewBag.notification = "successful";
return View("~/Views/User/settings.cshtml");
}
}
}
public async Task<List<string>> AsyncModifyPassword(LoginDTO entity)
{
List<string> errors = new List<string>();
ApplicationUser user = await _userManager.FindByEmailAsync(entity.Email);
if (user == null)
{
errors.Add("User Not Found"); //todo, hablar sobre el tema de lanzar las excepciones
return errors;
}
//user.PasswordHash = _userManager.PasswordHasher.HashPassword(user, entity.Password);
IdentityResult result = await _userManager.ChangePasswordAsync(user, entity.Password , entity.NewPassword);
if (!result.Succeeded)
errors = result.Errors.ToList().Select(error => error.Description).ToList();
return errors;
}
Oui, vous avez raison. ResetPassword par jeton est une approche privilégiée. Il y a quelque temps, j'ai créé un wrapper complet. L'identité et le code NET peuvent être trouvés ici. Il pourrait être utile pour vous. Vous pouvez aussi trouver nuget ici. J'ai aussi expliqué la bibliothèque dans un blog ici. Ce wrapper est facilement consommable comme nuget et crée toutes les configurations nécessaires pendant l'installation.