2015-03-27 27 views
28

Ho bisogno di modificare la password per l'utente da admin. Quindi, admin non dovrebbe inserire una password attuale dell'utente, dovrebbe avere la possibilità di impostare una nuova password. Guardo il metodo ChangePasswordAsync, ma questo metodo richiede l'inserimento della vecchia password. Quindi, questo metodo non è appropriato per questa attività. Perciò l'ho fatto dal modo seguente:Password cambio identità ASP.NET

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

funziona, ma teoricamente possiamo ricevere errore sul metodo AddPasswordAsync. Quindi, la vecchia password verrà rimossa ma la nuova non è impostata. Non è buono. Un modo per farlo in "una sola transazione"? PS. Ho visto il metodo ResetPasswordAsync con il token di reset, sembra, è più sicuro (perché non può essere una situazione instabile con l'utente), ma in ogni caso lo fa con 2 azioni.

+0

Il punto cruciale di questa domanda è farlo in un'unica transazione. Saresti soddisfatto di farlo in due transazioni e di continuare a provare fino a quando il secondo riesce? In caso contrario, potrebbe essere necessario scrivere la propria implementazione della modifica di una password. –

risposta

20

ApplicationUserManager è la classe generata dal modello ASP.NET.

Il che significa che è possibile modificarlo e aggiungere qualsiasi funzionalità che non ha ancora. La classe UserManager ha una proprietà protetta denominata Store che memorizza un riferimento alla classe UserStore (oa qualsiasi sottoclasse di essa, a seconda di come è stata configurata l'identità ASP.NET o se si utilizzano implementazioni personalizzate degli user store, ad esempio se si utilizza un motore di database diverso come 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 }); 
     } 

     var newPasswordHash = this.PasswordHasher.HashPassword(newPassword); 

     await store.SetPasswordHashAsync(userId, newPasswordHash); 
     return Task.FromResult<IdentityResult>(IdentityResult.Success); 
    } 
} 

Il UserManager non è altro che un involucro del sottostante UserStore. Consulta la documentazione dell'interfaccia IUserPasswordStore allo MSDN sui metodi disponibili.

Edit: Il PasswordHasher è anche una proprietà pubblica della classe UserManager, vedere interface definition here.

+0

Questo è un approccio interessante. Mi chiedo cosa potremmo implementare, in modo che la password venga modificata in una transazione, anche se l'amministratore non conosce la password originale. –

+1

Questo approccio è una transazione. Non è necessaria una vecchia password, poiché la nuova implementazione non utilizza alcuna funzione di quella vecchia e chiama direttamente "SetPasswordHashAsync" dall'implementazione di "UserStore', se ce n'è. Tutto quello che devi sapere è 'userId' e' newPassword' (che puoi generare casualmente) – Tseng

30

Questo metodo ha funzionato per me:

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(); 
} 
+5

Anche se questo codice potrebbe funzionare, una buona risposta dovrebbe anche spiegare come funziona e perché è una buona soluzione. – Blackwood

+2

metodi di modifica delle password predefiniti nella classe UserManager prima provare a convalidare la password ed eseguire i controlli di sicurezza ma ho aggiornato il valore della password direttamente come un campo ordinario che non ha alcun vincolo speciale su di esso. –

+0

Che cos'è UsercredentialsModel? –

3

Questo è solo un affinamento sulla risposta fornita da @Tseng. (Ho dovuto modificarlo per farlo funzionare).

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

Nota: ciò vale in particolare per una configurazione modificata che utilizza int come chiavi primarie per utenti e ruoli. Credo che si tratterebbe semplicemente di rimuovere gli argomenti di tipo <AppUser, int> per farlo funzionare con l'impostazione Identità ASP.NET predefinita.

0
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"); 
      } 
     } 
} 
-2

Sì, hai ragione. ResetPassword tramite token è un approccio preferito. Qualche tempo fa ho creato un wrapper completo su .NET Identity e il codice può essere trovato here. Potrebbe essere utile per te. È inoltre possibile trovare nuget here. Ho anche spiegato la biblioteca in un blog here. Questo wrapper è facilmente consumabile come nuget e crea tutte le configurazioni necessarie durante l'installazione.

+0

Preferisco risposte con contenuto in esse anziché collegamenti esterni. Se quei collegamenti dovessero mai morire per qualsiasi motivo, questa risposta sarebbe del tutto inutile. Inoltre, stai rispondendo a una domanda che ha più di due anni con una risposta accettata due anni dopo senza fornire alcun valore aggiuntivo. – shortstuffsushi

11

EDIT: So che l'OP ha richiesto una risposta che esegue l'operazione in una transazione, ma penso che il codice sia utile per le persone.

Tutte le risposte utilizzano direttamente il PasswordHasher che non è una buona idea in quanto si perde un po 'di funzionalità (convalida ecc.).

Un'alternativa (e presumibilmente l'approccio consigliato) consiste nel creare un token di reimpostazione della password e utilizzarlo per modificare la password.Esempio:

var user = await UserManager.FindByIdAsync(id); 

var token = await UserManager.GeneratePasswordResetTokenAsync(user); 

var result = await UserManager.ResetPasswordAsync(user, token, "[email protected]"); 
+0

Questo sembra essere il modo giusto e apre molte nuove possibilità. Grazie! –

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

Nota: qui UserId sto Recupero dalla corrente registrato utente.

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

    } 
+0

In genere, le risposte sono molto più utili se includono una spiegazione di cosa è inteso fare il codice e perché questo risolve il problema senza introdurre altri. –

Problemi correlati