ASP.NET Identity change password

2019-01-07 06:50发布

I need ability to change password for user by admin. So, admin should not enter a current password of user, he should have ability to set a new password. I look at ChangePasswordAsync method, but this method requires to enter old password. So, this method is not appropriate for this task. Therefore I have made it by the following way:

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

it works, but theoretically we can receive error on AddPasswordAsync method. So, old password will be removed but new is not set. It's not good. Any way to do it in "one transaction"? PS. I seen ResetPasswordAsync method with reset token, seems, it's more safe (because can't be unstable situation with user) but in any case, it does by 2 actions.

7条回答
姐就是有狂的资本
2楼-- · 2019-01-07 07:21
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 : here UserId i am retrieving from Current logged User.

查看更多
孤傲高冷的网名
3楼-- · 2019-01-07 07:22
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");
            }
      }
 }
查看更多
祖国的老花朵
4楼-- · 2019-01-07 07:24

EDIT: I know the OP requested an answer which performs the task in one transaction but I think the code is useful to people.

All the answers use the PasswordHasher directly which isn't a good idea as you will lose some baked in functionality (validation etc).

An alternative (and I would assume the recommended approach) is to create a password reset token and then use that to change the password. Example:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
查看更多
We Are One
5楼-- · 2019-01-07 07:32

This method worked for 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();
}
查看更多
成全新的幸福
6楼-- · 2019-01-07 07:32

This is just a refinement on the answer provided by @Tseng. (I had to tweak it to get it to work).

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: this applies specifically to a modified setup that uses int as the primary keys for users and roles. I believe it would simply be a matter of removing the <AppUser, int> type args to get it to work with the default ASP.NET Identity setup.

查看更多
我命由我不由天
7楼-- · 2019-01-07 07:43

Yes, you are correct. ResetPassword through token is a preferred approach. Sometime back, I created a complete wrapper over .NET Identity and code can be found here. It might be helpful for you. You can also find nuget here. I also explained the library in a blog here. This wrapper is easily consumable as nuget and create all required configs during installation.

查看更多
登录 后发表回答