Сброс пароля

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 15.12.2019

Рассмотрим механизм сброса пароля в ASP.NET Core Identity и для этого продолжим работу с проектом из прошлой темы.

Сброс пароля можно сделать разными способами. В данном случае рассмотрим стандартный двухступенчатый процесс, когда любой пользователь (в том числе анонимный) может ввести свой адрес электронной почты, на который отправляется ссылка для сбоса пароля. Пользователь переходит по этой ссылке и вводит в нужное поле новый пароль. Такой процесс разбивается на две стадии. На первой стадии пользователю нужно ввести электронный адрес для отправки ссылки сброса пароля, система получает этот адрес и отправляет на него ссылку. И вторая стадия - пользователю нужно ввести новый пароль, а системе установить введенный парол в качестве текущего.

Для реализации данной системы в проект в папку Models добавим две модели. Первая модель будет называться ForgotPasswordViewModel и будет предназначена для ввода адреса для отправки ссылки дял сброса пароля:

using System.ComponentModel.DataAnnotations;

namespace EmailApp.Models
{
    public class ForgotPasswordViewModel
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; }
    }
}

Вторая модель будет называться ResetPasswordViewModel и будет применяться собственно дял сброса пароля:

using System.ComponentModel.DataAnnotations;

namespace EmailApp.Models
{
    public class ResetPasswordViewModel
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "Пароль должен содержать как минимум 6 символов", MinimumLength = 6)]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "Пароли не совпадают")]
        public string ConfirmPassword { get; set; }

        public string Code { get; set; }
    }
}

Сначала определим действия для отправки ссылки для сброса пароля. Для этого в контроллер AccountController добавим два метода:

[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword()
{
	return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
	if (ModelState.IsValid)
	{
		var user = await _userManager.FindByEmailAsync(model.Email);
		if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
		{
			// пользователь с данным email может отсутствовать в бд
			// тем не менее мы выводим стандартное сообщение, чтобы скрыть 
			// наличие или отсутствие пользователя в бд
			return View("ForgotPasswordConfirmation");
		}

		var code = await _userManager.GeneratePasswordResetTokenAsync(user);
		var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
		EmailService emailService = new EmailService();
		await emailService.SendEmailAsync(model.Email, "Reset Password",
			$"Для сброса пароля пройдите по ссылке: <a href='{callbackUrl}'>link</a>");
		return View("ForgotPasswordConfirmation");
	}
	return View(model);
}

Для сброса пароля пользователь будет обращаться к методу ForgotPassword, который будет вызывать представление ForgotPassword.cshtml. Поэтому добавим в папку Views/Account добавим представление ForgotPassword.cshtml:

@model ForgotPasswordViewModel
@{
    ViewData["Title"] = "Забыли пароль?";
}

<h2>@ViewData["Title"]</h2>
<form asp-controller="Account" asp-action="ForgotPassword" method="post" class="form-horizontal">
    <h4>Введите email.</h4>
    <hr />
    <div asp-validation-summary="All" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Email" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Email" class="form-control" />
            <span asp-validation-for="Email" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="submit" class="btn btn-primary">Отправить</button>
        </div>
    </div>
</form>

Данное представление содержит форму ввода электронного адреса. После отправки данные уходят POST-методу ForgotPassword. В нем генеируется токен сброса пароля для данного пользователя и с помощью класса EmailService из прошлой темы отправляется письмо на веденный электронный адрес. После отправки пользователю возвращается представление ForgotPasswordConfirmation. Поэтому добавим в папку Views/Account добавим представление ForgotPasswordConfirmation.cshtml:

@{
    ViewData["Title"] = "Сбросить пароль";
}

<h2>@ViewData["Title"]</h2>
<p>
    Для сброса пароля перейдите по ссылке в письме, отправленном на ваш email.
</p>

Теперь определим функционал собственно для сброса пароля. Ссылка, которая отправляется в письме, будет указывать на действие ResetPassword контроллера Account. Поэтому добавим в контроллер AccountController еще пару методов:

[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null)
{
	return code == null ? View("Error") : View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
	if (!ModelState.IsValid)
	{
		return View(model);
	}
	var user = await _userManager.FindByEmailAsync(model.Email);
	if (user == null)
	{
		return View("ResetPasswordConfirmation");
	}
	var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
	if (result.Succeeded)
	{
		return View("ResetPasswordConfirmation");
	}
	foreach (var error in result.Errors)
	{
		ModelState.AddModelError(string.Empty, error.Description);
	}
	return View(model);
}

При переходе по ссылке пользователь будет обращаться к методу ResetPassword, который получит токен сброса пароля из ссылки и возвратить представление ResetPassword.cshtml. Добавим в папку Views/Account представление ResetPassword.cshtml:

@model ResetPasswordViewModel
@{
    ViewData["Title"] = "Сброс пароля";
}

<h2>@ViewData["Title"]</h2>
<h4>Введите пароль.</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <input asp-for="Code" type="hidden" />
            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ConfirmPassword"></label>
                <input asp-for="ConfirmPassword" class="form-control" />
                <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Сбросить</button>
        </form>
    </div>
</div>

Данное представление содержит форму для ввода нового пароля. После отправки этой формы POST-метод ResetPassword получает данные и, если пользователь с указанным email существует, то с помощью метода _userManager.ResetPasswordAsync() выполняет переустановку пароля для этого пользователя.

При удачной установке нового пароля пользователю возвращается представление ResetPasswordConfirmation. Поэтому добавим также в папку Views/Account представление ResetPasswordConfirmation.cshtml:

@{
    ViewData["Title"] = "Подтверждение сброса пароля";
}

<h2>@ViewData["Title"]</h2>
<p>
    Ваш пароль сброшен. Для входа в приложение перейдите по <a asp-action="Login">ссылке</a>.
</p>

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

Сброс пароля в ASP NET Core Identity

И после этого мы будем перенаправлены по методу ForgetPasswordConfirmation:

Восстановление пароля в ASP NET Core Identity

Тем временем на указанную электронную почту приходит письмо с ссылкой. После перехода по этой ссылке срабатывает метод ResetPassword, и мы оказываемся на странице сброса пароля:

Напоминание пароля в ASP NET Identity Core

Указываем ту же почту и новый пароль и отправляем форму на сервер. После этого post-метод ResetPassword обрабатывает данные и перенаправляет нас на метод ResetPasswordConfirmation::

Сброс пароля в ASP NET Core Identity

И после переустановки пароля мы сможем заходить на сайт под тем же электронным адресом и новым паролем.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850