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:44

ApplicationUserManager is the class generated by the ASP.NET Template.

Which means, you can edit it and add any functionality it doesn't have yet. The UserManager class has a protected property named Store which stores a reference to the UserStore class (or any subclass of it, depending on how you configured your ASP.NET Identity or if you use custom user store implementations, i.e. if you use different database engine like 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);
    }
}

The UserManager is nothing else than a wrapper to the underlying UserStore. Check out IUserPasswordStore interface documentation at MSDN on available Methods.

Edit: The PasswordHasher is also a public property of the UserManager class, see interface definition here.

Edit 2: Since some people naively believe, you can't do password validation this way, I've updated it. The PasswordHasher property is also a property of UserManager and its as simple as adding 2 lines of code to add password validation too (which wasn't an requirement of the original question though).

查看更多
登录 后发表回答