Создание безопасного веб-приложения ASP.NET MVC 5 с входом, подтверждением электронной почты и сбросом пароля (C#)
В этом руководстве показано, как создать веб-приложение ASP.NET MVC 5 с подтверждением электронной почты и сбросом пароля с помощью системы членства ASP.NET Identity.
Обновленную версию этого руководства, в котором используется .NET Core, см. в статье Подтверждение учетной записи и восстановление пароля в ASP.NET Core.
Создание приложения ASP.NET MVC
Начните с установки и запуска Visual Studio Express 2013 для Web или Visual Studio 2013. Установите Visual Studio 2013 с обновлением 3 или более поздней версии.
Примечание
Предупреждение. Для работы с этим руководством необходимо установить Visual Studio 2013 обновление 3 или более поздней версии.
Создайте веб-проект ASP.NET и выберите шаблон MVC. веб-формы также поддерживает ASP.NET Identity, поэтому вы можете выполнить аналогичные действия в приложении веб-форм.
Оставьте для проверки подлинности по умолчанию значение Индивидуальные учетные записи пользователей. Если вы хотите разместить приложение в Azure, оставьте флажок проверка. Далее в этом руководстве мы развернем его в Azure. Вы можете открыть учетную запись Azure бесплатно.
Задайте для проекта использование SSL.
Запустите приложение, щелкните ссылку Зарегистрировать и зарегистрируйте пользователя. На этом этапе единственная проверка сообщения электронной почты выполняется с помощью атрибута [EmailAddress] .
В Обозреватель сервера перейдите в раздел Подключения к данным\DefaultConnection\Tables\AspNetUsers, щелкните правой кнопкой мыши и выберите Открыть определение таблицы.
На следующем рисунке показана
AspNetUsers
схема:Щелкните правой кнопкой мыши таблицу AspNetUsers и выберите Показать данные таблицы.
На данный момент сообщение электронной почты не подтверждено.Щелкните строку и выберите удалить. Вы добавите это сообщение еще раз на следующем шаге и отправите сообщение электронной почты с подтверждением.
Подтверждение Email
Рекомендуется подтвердить адрес электронной почты новой регистрации пользователя, чтобы убедиться, что он не олицетворяет кого-то другого (т. е. он не зарегистрирован с чужой электронной почтой). Предположим, у вас есть форум для обсуждения, и вы хотите запретить "bob@example.com"
регистрацию в качестве "joe@contoso.com"
. Без подтверждения "joe@contoso.com"
сообщения электронной почты может получать нежелательные сообщения электронной почты из вашего приложения. Предположим, Что Боб случайно зарегистрировался как и не заметил этого, он не сможет использовать восстановление пароля, так как "bib@example.com"
приложение не имеет правильного адреса электронной почты. Email подтверждение обеспечивает только ограниченную защиту от ботов и не обеспечивает защиту от определенных спамеров, у них есть много рабочих псевдонимов электронной почты, которые они могут использовать для регистрации.
Как правило, вы хотите запретить новым пользователям публиковать какие-либо данные на веб-сайте, прежде чем они будут подтверждены электронной почтой, SMS-сообщением или другим механизмом. В разделах ниже мы включим подтверждение по электронной почте и изменим код, чтобы предотвратить вход новых зарегистрированных пользователей до тех пор, пока их электронная почта не будет подтверждена.
Подключение SendGrid
Инструкции в этом разделе не являются актуальными. Обновленные инструкции см. в разделе Настройка поставщика электронной почты SendGrid .
Хотя в этом руководстве показано, как добавлять уведомления по электронной почте только с помощью SendGrid, вы можете отправлять сообщения электронной почты с помощью SMTP и других механизмов (см. дополнительные ресурсы).
В консоли диспетчера пакетов введите следующую команду.
Install-Package SendGrid
Перейдите на страницу регистрации Azure SendGrid и зарегистрируйтесь для получения бесплатной учетной записи SendGrid. Настройте SendGrid, добавив код, аналогичный следующему в App_Start/IdentityConfig.cs:
public class EmailService : IIdentityMessageService { public async Task SendAsync(IdentityMessage message) { await configSendGridasync(message); } // Use NuGet to install SendGrid (Basic C# client lib) private async Task configSendGridasync(IdentityMessage message) { var myMessage = new SendGridMessage(); myMessage.AddTo(message.Destination); myMessage.From = new System.Net.Mail.MailAddress( "Joe@contoso.com", "Joe S."); myMessage.Subject = message.Subject; myMessage.Text = message.Body; myMessage.Html = message.Body; var credentials = new NetworkCredential( ConfigurationManager.AppSettings["mailAccount"], ConfigurationManager.AppSettings["mailPassword"] ); // Create a Web transport for sending email. var transportWeb = new Web(credentials); // Send the email. if (transportWeb != null) { await transportWeb.DeliverAsync(myMessage); } else { Trace.TraceError("Failed to create Web transport."); await Task.FromResult(0); } } }
Вам потребуется добавить следующие компоненты:
using SendGrid;
using System.Net;
using System.Configuration;
using System.Diagnostics;
Чтобы сделать этот пример простым, мы сохраним параметры приложения в файлеweb.config :
</connectionStrings>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<!-- Markup removed for clarity. -->
<add key="mailAccount" value="xyz" />
<add key="mailPassword" value="password" />
</appSettings>
<system.web>
Предупреждение
Безопасность— никогда не сохраняйте конфиденциальные данные в исходном коде. Учетная запись и учетные данные хранятся в appSetting. В Azure эти значения можно безопасно хранить на вкладке Настройка в портал Azure. Ознакомьтесь с рекомендациями по развертыванию паролей и других конфиденциальных данных в ASP.NET и Azure.
Включение подтверждения электронной почты в контроллере учетной записи
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id,
"Confirm your account", "Please confirm your account by clicking <a href=\""
+ callbackUrl + "\">here</a>");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Убедитесь, что файл Views\Account\ConfirmEmail.cshtml имеет правильный синтаксис razor. ( Символ @в первой строке может отсутствовать. )
@{
ViewBag.Title = "Confirm Email";
}
<h2>@ViewBag.Title.</h2>
<div>
<p>
Thank you for confirming your email. Please @Html.ActionLink("Click here to Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
</p>
</div>
Запустите приложение и щелкните ссылку Зарегистрировать. После отправки формы регистрации вы войдете в систему.
Проверьте учетную запись электронной почты и щелкните ссылку, чтобы подтвердить адрес электронной почты.
Требовать подтверждение по электронной почте перед вхоской
В настоящее время после заполнения формы регистрации пользователь входит в систему. Как правило, вы хотите подтвердить его адрес электронной почты, прежде чем войти в систему. В приведенном ниже разделе мы изменим код, чтобы новые пользователи имели подтвержденное сообщение электронной почты, прежде чем они будут входить в систему (проходить проверку подлинности). Обновите HttpPost Register
метод, указав следующие выделенные изменения:
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// Comment the following line to prevent log in until the user is confirmed.
// await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Confirm your account",
"Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
// Uncomment to debug locally
// TempData["ViewBagLink"] = callbackUrl;
ViewBag.Message = "Check your email and confirm your account, you must be confirmed "
+ "before you can log in.";
return View("Info");
//return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Если закомментировать SignInAsync
метод, пользователь не будет входить в систему с помощью регистрации. Строку TempData["ViewBagLink"] = callbackUrl;
можно использовать для отладки приложения и проверки регистрации без отправки сообщения электронной почты. ViewBag.Message
используется для отображения инструкций подтверждения. Пример загрузки содержит код для проверки подтверждения электронной почты без настройки электронной почты, а также может использоваться для отладки приложения.
Создайте Views\Shared\Info.cshtml
файл и добавьте следующую разметку razor:
@{
ViewBag.Title = "Info";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>
Добавьте атрибут Authorize в Contact
метод действия контроллера Home. Вы можете щелкнуть ссылку Контакт , чтобы убедиться, что анонимные пользователи не имеют доступа и пользователи, прошедшие проверку подлинности, имеют доступ.
[Authorize]
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
Необходимо также обновить HttpPost Login
метод действия:
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Require the user to have a confirmed email before they can log on.
var user = await UserManager.FindByNameAsync(model.Email);
if (user != null)
{
if (!await UserManager.IsEmailConfirmedAsync(user.Id))
{
ViewBag.errorMessage = "You must have a confirmed email to log on.";
return View("Error");
}
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Обновите представление Views\Shared\Error.cshtml , чтобы отобразить сообщение об ошибке:
@model System.Web.Mvc.HandleErrorInfo
@{
ViewBag.Title = "Error";
}
<h1 class="text-danger">Error.</h1>
@{
if (String.IsNullOrEmpty(ViewBag.errorMessage))
{
<h2 class="text-danger">An error occurred while processing your request.</h2>
}
else
{
<h2 class="text-danger">@ViewBag.errorMessage</h2>
}
}
Удалите все учетные записи в таблице AspNetUsers , содержащие псевдоним электронной почты, с которым вы хотите проверить. Запустите приложение и убедитесь, что вы не можете войти в систему, пока не подтвердите свой адрес электронной почты. После подтверждения адреса электронной почты щелкните ссылку Контакт .
Восстановление и сброс пароля
Удалите символы комментариев из HttpPost ForgotPassword
метода действия в контроллере учетной записи:
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
Удалите символы комментариев из ForgotPassword
ActionLink в файле представления Razor Views\Account\Login.cshtml :
@using MvcPWy.Models
@model LoginViewModel
@{
ViewBag.Title = "Log in";
}
<h2>@ViewBag.Title.</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Use a local account to log in.</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
<p>
@Html.ActionLink("Register as a new user", "Register")
</p>
@* Enable this once you have account confirmation enabled for password reset functionality *@
<p>
@Html.ActionLink("Forgot your password?", "ForgotPassword")
</p>
}
</section>
</div>
<div class="col-md-4">
<section id="socialLoginForm">
@Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl })
</section>
</div>
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
На странице Вход теперь отображается ссылка для сброса пароля.
Ссылка для подтверждения повторной отправки сообщения электронной почты
После того как пользователь создает новую локальную учетную запись, ей по электронной почте будет отправлена ссылка подтверждения, с помощью которых он должен использовать, прежде чем сможет войти в систему. Если пользователь случайно удалит сообщение электронной почты с подтверждением или сообщение электронной почты никогда не поступает, ей потребуется отправить ссылку подтверждения. В следующих изменениях кода показано, как включить эту функцию.
Добавьте следующий вспомогательный метод в нижнюю часть файла Controllers\AccountController.cs :
private async Task<string> SendEmailConfirmationTokenAsync(string userID, string subject)
{
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = userID, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(userID, subject,
"Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
return callbackUrl;
}
Обновите метод Register, чтобы использовать новый вспомогательный метод:
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// Comment the following line to prevent log in until the user is confirmed.
// await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account");
ViewBag.Message = "Check your email and confirm your account, you must be confirmed "
+ "before you can log in.";
return View("Info");
//return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Обновите метод Login, чтобы повторно отправить пароль, если учетная запись пользователя не была подтверждена:
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Require the user to have a confirmed email before they can log on.
// var user = await UserManager.FindByNameAsync(model.Email);
var user = UserManager.Find(model.Email, model.Password);
if (user != null)
{
if (!await UserManager.IsEmailConfirmedAsync(user.Id))
{
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account-Resend");
// Uncomment to debug locally
// ViewBag.Link = callbackUrl;
ViewBag.errorMessage = "You must have a confirmed email to log on. "
+ "The confirmation token has been resent to your email account.";
return View("Error");
}
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Объединение учетных записей для входа в социальных сетях и локальных учетных записей
Вы можете объединить локальные учетные записи и учетные записи социальных параметров, щелкнув ссылку электронной почты. В следующей последовательности RickAndMSFT@gmail.com сначала создается локальное имя входа, но вы можете сначала создать учетную запись в качестве социального входа, а затем добавить локальное имя входа.
Щелкните ссылку Управление . Обратите внимание на внешние имена входа: 0 , связанные с этой учетной записью.
Щелкните ссылку на другую службу входа и примите запросы приложения. Две учетные записи были объединены, и вы сможете войти в систему с помощью любой из них. Вы можете захотеть, чтобы пользователи добавляли локальные учетные записи в случае, если служба проверки подлинности в социальных сетях не работает или, скорее всего, они потеряли доступ к своей учетной записи социальной сети.
На следующем изображении Tom — это вход в социальную сеть (который можно увидеть на странице Внешние имена входа: 1 ).
Щелкнув Выбрать пароль , вы можете добавить локальный вход, связанный с той же учетной записью.
Email более подробное подтверждение
Дополнительные сведения см. в руководстве по подтверждению учетной записи и восстановлению пароля с помощью ASP.NET identity .
Отладка приложения
Если вы не получили сообщение электронной почты со ссылкой:
- Проверьте папку нежелательной почты или спама.
- Войдите в учетную запись SendGrid и щелкните ссылку Email Действие.
Чтобы проверить ссылку для проверки без электронной почты, скачайте готовый пример. Ссылка для подтверждения и коды подтверждения будут отображаться на странице.
Дополнительные ресурсы
- Ссылки на ресурсы ASP.NET Identity Recommended
- Подтверждение учетной записи и восстановление пароля с помощью удостоверения ASP.NET Дополнительные сведения о восстановлении пароля и подтверждении учетной записи.
- Вход в приложение MVC 5 с Facebook, Twitter, LinkedIn и Google OAuth2 В этом руководстве показано, как написать приложение ASP.NET MVC 5 с авторизацией OAuth 2 и Facebook. В ней также показано, как добавить дополнительные данные в базу данных удостоверений.
- Развертывание приложения Secure ASP.NET MVC с членством, OAuth и База данных SQL в Azure. В этом руководстве описано развертывание Azure, защита приложения с помощью ролей, использование API членства для добавления пользователей и ролей, а также дополнительные функции безопасности.
- Создание приложения Google для OAuth 2 и подключение приложения к проекту
- Создание приложения в Facebook и подключение приложения к проекту
- Настройка SSL в проекте
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по