84

我需要管理员更改用户密码的能力。所以,管理员不应该输入用户的当前密码,他应该有能力设置一个新的密码。我看一下 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 方法上收到错误。因此,旧密码将被删除,但未设置新密码。这样不好。有什么办法可以在“一次交易”中做到这一点?PS。我看到了带有重置令牌的 ResetPasswordAsync 方法,似乎它更安全(因为用户不会出现不稳定的情况),但无论如何,它通过 2 个动作来完成。

4

11 回答 11

127

编辑:我知道 OP 要求在一个事务中执行任务的答案,但我认为该代码对人们有用。

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

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

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
于 2017-08-16T14:07:31.657 回答
67

这种方法对我有用:

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();
}
于 2015-12-24T21:10:10.507 回答
44

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请参见此处的接口定义

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

于 2015-03-27T00:56:53.983 回答
14

在 .net 核心 3.0 中

var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, password);
于 2020-02-25T21:19:54.483 回答
7

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

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用作用户和角色主键的修改设置。我相信只需删除<AppUser, int>类型 args 即可使其与默认的 ASP.NET 标识设置一起使用。

于 2016-06-16T17:48:19.633 回答
4

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

  1. 生成密码令牌,
  2. 使用生成的 Token 重置密码...
public async Task<IdentityResult> ResetPasswordAsync(ApplicationUser user, string password)
{
    string token = await userManager.GeneratePasswordResetTokenAsync(user);
    return await userManager.ResetPasswordAsync(user, token, password);
}
于 2021-04-28T19:32:23.487 回答
3
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 我正在从当前登录的用户中检索。

于 2017-11-16T03:07:17.417 回答
2

如果您没有用户的当前密码,但仍想更改密码。你可以做什么,而不是先删除用户的密码,然后添加新密码。这样您就可以更改用户的密码,而无需该用户的当前密码。

await UserManager.RemovePasswordAsync(user);
await UserManager.AddPasswordAsync(user, model.Password);
于 2019-07-04T09:42:00.757 回答
1

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

PasswordValidator不再是一个属性UserManager- 相反,该属性是一个IList PasswordValidators. 此外,Identity 现在有一种protected UpdatePasswordHash无需直接访问即可为您更改密码的方法UserStore,这消除了手动散列和保存密码的需要。

UserManager还有一个公共属性 ,bool SupportsUserPassword它取代了测试 ifStore实现的需要IUserPasswordStore(在内部,这正是getterUserManager中所做的)。SupportsUserPassword

既然UpdatePasswordHashprotected,您仍然需要扩展基础UserManager。它的签名是:

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

wherevalidatePassword表示是否运行密码验证。不幸的是,这并不默认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通过验证更新该用户的密码。如果更新成功,您仍然需要保存用户调用。ApplicationUsertrueUpdateAsync

于 2020-08-21T17:04:34.920 回答
-1
public async Task<ActionResult> ResetUserPassword(string id, string Password)
{
    //     Find User
    var user = await context.Users.Where(x => x.Id == id).SingleOrDefaultAsync();
    if (user == null)
    {
        return RedirectToAction("UserList");
    }
    await UserManager.RemovePasswordAsync(id);
    //     Add a user password only if one does not already exist
    await UserManager.AddPasswordAsync(id, Password);
    return RedirectToAction("UserDetail", new { id = id });
}
于 2020-02-14T20:12:36.737 回答
-2

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

于 2017-07-03T17:33:51.480 回答