ASP.NET标识更改密码

时间:2015-03-27 00:36:07

标签: c# asp.net-mvc-5 asp.net-identity-2 reset-password

我需要能够通过管理员更改用户的密码。因此,管理员不应输入用户的当前密码,他应该有能力设置新密码。我查看ChangePasswordAsync方法,但此方法需要输入旧密码。因此,此方法不适合此任务。因此我通过以下方式做到了:

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

它可以工作,但理论上我们可以在AddPasswordAsync方法上收到错误。因此,旧密码将被删除,但未设置新密码。这不好。任何方式在&#34;一个交易&#34;? PS。我看到ResetPasswordAsync方法带有重置令牌,似乎更安全(因为用户不能处于不稳定状态),但无论如何,它通过2个动作完成。

12 个答案:

答案 0 :(得分:46)

这种方法对我有用:

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

答案 1 :(得分:36)

编辑:我知道OP请求了一个在一次交易中执行任务的答案,但我认为代码对人们有用。

所有答案都直接使用PasswordHasher,这不是一个好主意,因为你将失去一些功能(验证等)。

另一种选择(我会假设推荐的方法)是创建密码重置令牌,然后使用它来更改密码。例如:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");

答案 2 :(得分:27)

ApplicationUserManager是ASP.NET模板生成的类。

这意味着,您可以编辑它并添加它尚未拥有的任何功能。 UserManager类有一个名为Store的受保护属性,它存储对UserStore类(或其任何子类)的引用,具体取决于您配置ASP.NET标识的方式或是否使用自定义用户存储实现,即如果您使用不同的数据库引擎,如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只不过是底层UserStore的包装器。查看MSDN关于可用方法的IUserPasswordStore界面文档。

修改 PasswordHasher也是UserManager类的公共属性,请参阅interface definition here

编辑2: 由于有些人天真地相信,你不能用这种方式进行密码验证,我已经更新了。 PasswordHasher属性也是UserManager的属性,就像添加2行代码以添加密码验证一样简单(虽然这不是原始问题的要求)。

答案 3 :(得分:7)

在.net core 3.0中

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

答案 4 :(得分:5)

这只是对@Tseng提供的答案的改进。 (我不得不调整它以使其工作)。

id

注意:这特别适用于使用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; } } 作为用户和角色主键的修改后的设置。我相信这只是删除int类型args以使其与默认的ASP.NET标识设置一起使用的问题。

答案 5 :(得分: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; }
    }

注意:此处我正在从当前登录用户中检索UserId。

答案 6 :(得分:1)

如果您没有用户的当前密码,但仍想更改密码。相反,您可以做的是先删除用户的密码,然后添加新密码。这样,您将能够更改用户密码而无需该用户的当前密码。

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

答案 7 :(得分:1)

对于ASP.NET Core 3.1用户,这是@Tseng和@BCA提供的出色答案的现代化迭代。

PasswordValidator不再是UserManager的属性-而是该属性为IList PasswordValidators。此外,Identity现在具有protected UpdatePasswordHash方法,无需直接访问UserStore即可为您更改密码,从而无需手动散列并保存密码。

UserManager还有一个公共属性bool SupportsUserPassword,它代替了测试Store是否实现IUserPasswordStore的需要(内部,这正是UserManagerSupportsUserPassword吸气剂中进行)。

由于UpdatePasswordHashprotected,因此您仍然需要扩展基础UserManager。它的签名是:

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

其中validatePassword代表是否运行密码验证。不幸的是,不是默认为true,因此需要提供它。实现看起来像这样:

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

像以前一样,首先要确保当前的UserStore支持密码。

然后,只需使用UpdatePasswordHash,新密码和ApplicationUser调用true,即可通过验证更新该用户的密码。如果更新成功,则仍然必须保存用户,因此请致电UpdateAsync

答案 8 :(得分:1)

我认为解决方案要容易得多

  1. 生成密码令牌,
  2. 使用生成的令牌重置密码...

 public async Task<IdentityResult> ResetPasswordAsync(ApplicationUser user, string password) {
            string token = await userManager.GeneratePasswordResetTokenAsync(user);
            return await userManager.ResetPasswordAsync(user, token, password);
        }

答案 9 :(得分: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");
            }
      }
 }

答案 10 :(得分:-1)

ListAPIView

答案 11 :(得分:-2)

是的,你是对的。通过令牌的ResetPassword是首选方法。 不久之后,我创建了一个完整的.NET Identity包装器,可以找到代码here。它可能对你有所帮助。您还可以找到nuget here。我还在博客here中解释了这个库。这个包装器很容易作为nuget使用,并在安装过程中创建所有必需的配置。