ASP.NET标识更改密码

Ole*_* Sh 49 c# reset-password asp.net-mvc-5 asp.net-identity-2

我需要能够通过管理员更改用户的密码.因此,管理员不应输入用户的当前密码,他应该有能力设置新密码.我查看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);
    }
Run Code Online (Sandbox Code Playgroud)

它有效,但理论上我们可以在AddPasswordAsync方法上收到错误.因此,旧密码将被删除,但未设置新密码.这不好.有什么方法可以在"一次交易"中做到吗?PS.我看到ResetPasswordAsync方法带有重置令牌,似乎,它更安全(因为不能与用户不稳定的情况)但无论如何,它通过2个动作.

bry*_*n c 52

这个方法对我有用:

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();
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然这段代码可能很有用,但一个好的答案也应该解释它是如何工作的以及为什么它是一个很好的解决方案. (7认同)
  • UserManager类中的默认更改密码方法首先尝试验证密码并进行安全检查,但我直接更新密码值,就像没有特殊约束的普通字段一样. (2认同)
  • 虽然我觉得这不是更改密码的正确方法,但它在无法生成 ResetToken 和验证令牌的不同情况下帮助了我。 (2认同)

Lee*_*unn 46

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

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

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

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案。需要注意的一件事:如果您没有注册令牌提供程序,它将抛出异常。我必须将 `.AddDefaultTokenProviders()` 添加到 Program.cs 中的 `IdentityBuilder` 流畅链中(例如链接在 `builder.Services.AddIdentityCore` 之后) (6认同)
  • 这似乎是正确的方式,并开辟了许多新的可能性.谢谢! (4认同)
  • 我更喜欢这个答案 (3认同)
  • 您当然也可以使用 UserManager 中定义的 `PasswordValidator` 属性在设置之前对其进行验证。但这不是原始问题的要求 (2认同)

Tse*_*eng 33

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

这意味着,您可以编辑它并添加它尚未拥有的任何功能.UserManager类有一个名为protected的属性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);
    }
}
Run Code Online (Sandbox Code Playgroud)

UserManager不是别的,只是一个包装底层UserStore.检查出IUserPasswordStore的接口文档MSDN上可用的方法.

编辑:PasswordHasher也是UserManager该类的公共属性,请参阅此处的接口定义.

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

  • 我使用了这个答案,但密码没有更新到数据库表,除非我在执行“SetPasswordHashAsync”方法后添加“await store.UpdateAsync(user,取消);”。PS 我对此进行了一些调整,_user_ 是“class ApplicationUser: IdentityUser”的对象。为什么会出现这种情况? (3认同)
  • 这种方法是一个事务。你不需要旧密码,因为新实现的不使用旧的任何功能,直接从“UserStore”的实现调用“SetPasswordHashAsync”,如果有的话。所有你需要知道的是 `userId` 和 `newPassword`(你可以随机生成) (2认同)

小智 13

我认为解决方案更容易

  1. 生成passwordToken,
  2. 使用生成的令牌重置密码...
public async Task<IdentityResult> ResetPasswordAsync(ApplicationUser user, string password)
{
    string token = await userManager.GeneratePasswordResetTokenAsync(user);
    return await userManager.ResetPasswordAsync(user, token, password);
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*vid 10

在 .net 核心 3.0 中

var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, password);
Run Code Online (Sandbox Code Playgroud)


BCA*_*BCA 6

这只是对@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:这特别适用于int用作用户和角色主键的已修改设置.我相信这只是删除<AppUser, int>类型args以使其与默认的ASP.NET标识设置一起使用的问题.