Пароль смены идентификатора ASP.NET

Мне нужно изменить пароль для пользователя по администратора. Таким образом, администратор не должен вводить текущий пароль пользователя, он должен иметь возможность устанавливать новый пароль. Я смотрю на метод 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 с токеном reset, кажется, он более безопасен (потому что не может быть нестабильной ситуацией с пользователем), но в любом случае он выполняет 2 действия.

Ответ 1

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 does not 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 о доступных методах.

Изменить: PasswordHasher также является публичным свойством класса UserManager, см. определение интерфейса здесь.

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

Ответ 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();
}

Ответ 3

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

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

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

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "[email protected]");

Ответ 4

Это просто уточнение ответа, предоставленного @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 в качестве основных ключей для пользователей и ролей. Я считаю, что просто нужно было бы удалить args <AppUser, int>, чтобы заставить его работать с установкой ASP.NET по умолчанию.

Ответ 5

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

Если у вас нет текущего пароля пользователя и вы все еще хотите сменить пароль. Что вы можете сделать вместо этого сначала удалить пароль пользователя, а затем добавить новый пароль. Таким образом, вы сможете изменить пароль пользователя без необходимости ввода текущего пароля этого пользователя.

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

Ответ 7

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

Ответ 8

 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;

    }

Ответ 9

Попробуйте удалить старый пароль и добавить новый пароль.

Пример кода:

  var userId = System.Web.HttpContext.Current.User.Identity.GetUserId();
  if (userId != null)
  {
    UserManager<IdentityUser> userManager =
    new UserManager<IdentityUser>(new UserStore<IdentityUser>());
    userManager.RemovePassword(userId);
    String newPassword = "aaaaaaa";
    userManager.AddPassword(userId, newPassword);
  }

Кредиты:

@Brando

Рекомендации:

UserManager RemovePassword: https://msdn.microsoft.com/en-us/library/dn497511(v=vs.108).aspx

UserManager AddPassword: https://msdn.microsoft.com/en-us/library/dn497465(v=vs.108).aspx

Ответ 10

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