ASP.NET Identity изменить пароль

Мне нужна возможность изменить пароль для пользователя администратором. Таким образом, администратор не должен вводить текущий пароль пользователя, он должен иметь возможность установить новый пароль. Я смотрю на метод ChangePasswordAsync, но этот метод требует ввода старого пароля. Таким образом, этот метод не подходит для данной задачи. Поэтому я сделал это следующим образом:--2-->

    [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. Таким образом, старый пароль будет удален, но новый не ставить. Это нехорошо. Любой способ сделать это в "одной транзакции"? ПС. Я видел метод ResetPasswordAsync с токеном reset, кажется, он более безопасен (потому что не может быть нестабильной ситуации с пользователем), но в любом случае он делает 2 действия.

8 ответов


ApplicationUserManager - класс, сгенерированный ASP.NET шаблон.

это означает, что вы можете отредактировать его и добавить любую функциональность, которой он еще не имеет. Класс UserManager имеет защищенное свойство с именем Store, который хранит ссылку на элемент UserStore class (или любой его подкласс, в зависимости от того, как вы настроили свой ASP.NET Identity или если вы используете пользовательские реализации хранилища пользователей, т. е. если вы используете другой компонент database engine, например 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. Проверьте IUserPasswordStore документация на интерфейс MSDN о доступных методов.

Edit: The PasswordHasher также является государственной собственностью UserManager класс, см. определение интерфейса здесь.

Edit 2: С некоторых людей наивно поверьте, вы не можете сделать проверку пароля таким образом, я обновил его. The PasswordHasher свойство также является собственность UserManager и его так же просто, как добавление 2 строк кода, чтобы добавить проверку пароля тоже (что не было требованием исходного вопроса, хотя).


этот метод работал для меня:

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

EDIT: я знаю, что OP запросил ответ, который выполняет задачу в одной транзакции, но я думаю, что код полезен людям.

все ответы используют PasswordHasher напрямую, что не является хорошей идеей, поскольку вы потеряете некоторые запеченные функции (проверка и т. д.).

альтернативой (и я бы предположил, что рекомендуемый подход) является создание токена сброса пароля, а затем использовать его для изменения пароля. Пример:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

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

Это просто уточнение ответа, предоставленного @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 настройка личности.


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 я получаю от текущего зарегистрированного пользователя.


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

 public async Task<List<string>> AsyncModifyPassword(LoginDTO entity)
    {
        List<string> errors = new List<string>();
        ApplicationUser user = await _userManager.FindByEmailAsync(entity.Email);
        if (user == null)
        {
            errors.Add("User Not Found"); //todo, hablar sobre el tema de lanzar las excepciones
            return errors;
        }

        //user.PasswordHash = _userManager.PasswordHasher.HashPassword(user, entity.Password);
        IdentityResult result = await _userManager.ChangePasswordAsync(user, entity.Password , entity.NewPassword);

        if (!result.Succeeded)
            errors = result.Errors.ToList().Select(error => error.Description).ToList();

        return errors;

    }

Да, вы правы. ResetPassword через токен является предпочтительным подходом. Некоторое время назад я создал полную оболочку над .NET Identity, и код можно найти здесь. Это может быть полезно для вас. Вы также можете найти nuget здесь. Я также объяснил библиотеку в блоге здесь. Эта оболочка легко расходуется как nuget и создает все необходимые конфигурации во время установки.