Пароль смены идентификатора 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 и создает все необходимые конфигурации во время установки.