Подтверждение учетной записи и восстановление пароля с помощью ASP.NET Identity (C#)
Перед выполнением этого руководства необходимо сначала выполнить создание безопасного веб-приложения ASP.NET MVC 5 с помощью входа, подтверждения электронной почты и сброса пароля. В этом руководстве содержатся дополнительные сведения и показано, как настроить электронную почту для подтверждения локальной учетной записи и разрешить пользователям сбрасывать забытый пароль в ASP.NET Identity.
Учетная запись локального пользователя требует, чтобы пользователь создавал пароль для учетной записи, и этот пароль хранится (безопасно) в веб-приложении. ASP.NET Identity также поддерживает учетные записи социальных параметров, которые не требуют от пользователя создания пароля для приложения. Учетные записи социальных сетей используют сторонние учетные записи (например, Google, Twitter, Facebook или Microsoft) для проверки подлинности пользователей. В этой статье рассматриваются следующие вопросы:
- Создайте приложение ASP.NET MVC и изучите функции ASP.NET identity.
- Создание примера удостоверения
- Настройка подтверждения по электронной почте
Новые пользователи регистрируют псевдоним электронной почты, который создает локальную учетную запись.
При нажатии кнопки Зарегистрировать на их адрес электронной почты отправляется сообщение электронной почты с подтверждением, содержащее маркер проверки.
Пользователю отправляется сообщение электронной почты с маркером подтверждения для своей учетной записи.
Если щелкнуть ссылку, вы подтвердите учетную запись.
Восстановление и сброс пароля
Локальные пользователи, которые забыли свой пароль, могут отправить маркер безопасности в свою учетную запись электронной почты, что позволит им сбросить пароль.
Вскоре пользователь получит сообщение электронной почты со ссылкой, позволяющей сбросить пароль.
Если щелкнуть ссылку, они перейдут на страницу Сброс.
Если нажать кнопку Сброс , пароль будет сброшен.
Создание веб-приложения ASP.NET
Начните с установки и запуска Visual Studio 2017.
Создайте веб-проект ASP.NET и выберите шаблон MVC. веб-формы также поддерживают ASP.NET Identity, поэтому вы можете выполнить аналогичные действия в приложении веб-форм.
Измените проверку подлинности на Индивидуальные учетные записи пользователей.
Запустите приложение, щелкните ссылку Регистрация и зарегистрируйте пользователя. На этом этапе единственная проверка сообщения электронной почты выполняется с помощью атрибута [EmailAddress] .
В Обозреватель сервера перейдите в раздел Подключения к данным\DefaultConnection\Tables\AspNetUsers, щелкните правой кнопкой мыши и выберите Открыть определение таблицы.
Схема показана на следующем рисунке
AspNetUsers
:Щелкните правой кнопкой мыши таблицу AspNetUsers и выберите Пункт Показать данные таблицы.
На данный момент сообщение электронной почты не подтверждено.
Хранилищем данных по умолчанию для ASP.NET Identity является Entity Framework, но его можно настроить для использования других хранилищ данных и добавления дополнительных полей. См. раздел Дополнительные ресурсы в конце этого руководства.
Класс запуска OWIN ( Startup.cs ) вызывается при запуске приложения и вызывает ConfigureAuth
метод в App_Start\Startup.Auth.cs, который настраивает конвейер OWIN и инициализирует ASP.NET Identity. Проверьте метод ConfigureAuth
. Каждый CreatePerOwinContext
вызов регистрирует обратный вызов (сохраненный OwinContext
в ), который будет вызываться один раз для каждого запроса для создания экземпляра указанного типа. Вы можете задать точку останова в конструкторе и Create
методе каждого типа (ApplicationDbContext, ApplicationUserManager
) и убедиться, что они вызываются при каждом запросе. Экземпляр ApplicationDbContext
и ApplicationUserManager
хранится в контексте OWIN, доступ к которому можно получить во всем приложении. ASP.NET перехватчики удостоверений в конвейер OWIN с помощью ПО промежуточного слоя для файлов cookie. Дополнительные сведения см. в разделе Управление жизненным циклом запросов для класса UserManager в ASP.NET Identity.
При изменении профиля безопасности создается новая метка безопасности и сохраняется в SecurityStamp
поле таблицы AspNetUsers . Обратите внимание, что SecurityStamp
поле отличается от файла cookie безопасности. Файл cookie безопасности не хранится в AspNetUsers
таблице (или где-либо еще в базе данных удостоверений). Маркер cookie безопасности самозаверяется с помощью DPAPI и создается с UserId, SecurityStamp
информацией о времени окончания срока действия и .
ПО промежуточного слоя для файлов cookie проверяет файл cookie при каждом запросе. Метод SecurityStampValidator
в классе попадает в Startup
базу данных и периодически проверяет метку безопасности, как указано в validateInterval
. Это происходит каждые 30 минут (в нашем примере), если вы не измените профиль безопасности. Для минимизации обращений к базе данных выбран 30-минутный интервал. Дополнительные сведения см. в руководстве по двухфакторной проверке подлинности .
Согласно комментариям в коде, метод поддерживает проверку подлинности UseCookieAuthentication
файлов cookie. Поле SecurityStamp
и связанный код обеспечивают дополнительный уровень безопасности для приложения. При смене пароля вы выйдете из браузера, с которым вы вошли. Метод SecurityStampValidator.OnValidateIdentity
позволяет приложению проверять маркер безопасности при входе пользователя, который используется при смене пароля или использовании внешнего имени входа. Это необходимо для того, чтобы все маркеры (файлы cookie), созданные со старым паролем, были недействительными. В примере проекта при изменении пароля пользователя для пользователя создается новый маркер, все предыдущие маркеры становятся недействительными, а SecurityStamp
поле обновляется.
Система удостоверений позволяет настроить приложение таким образом, чтобы при изменении профиля безопасности пользователей (например, при изменении пароля или изменении связанного имени входа (например, из Facebook, Google, учетной записи Майкрософт и т. д.) пользователь выходит из всех экземпляров браузера. Например, на рисунке ниже показан пример приложения "Единый выход ", который позволяет пользователю выйти из всех экземпляров браузера (в данном случае IE, Firefox и Chrome), нажав одну кнопку. Кроме того, этот пример позволяет выйти только из определенного экземпляра браузера.
В примере приложения "Единый выход " показано, как ASP.NET Identity позволяет повторно создать маркер безопасности. Это необходимо для того, чтобы все маркеры (файлы cookie), созданные со старым паролем, были недействительными. Эта функция обеспечивает дополнительный уровень безопасности для приложения; При изменении пароля вы будете выйдите из системы в том месте, где вы вошли в это приложение.
Файл App_Start\IdentityConfig.cs содержит классы ApplicationUserManager
и EmailService
SmsService
. Классы EmailService
и SmsService
реализуют IIdentityMessageService
интерфейс , поэтому в каждом классе есть общие методы для настройки электронной почты и SMS. Хотя в этом руководстве показано только, как добавить уведомления по электронной почте с помощью SendGrid, вы можете отправлять сообщения с помощью SMTP и других механизмов.
Класс Startup
также содержит котловую пластину для добавления входа в социальные сети (Facebook, Twitter и т. д.). Дополнительные сведения см. в руководстве по приложению MVC 5 с Facebook, Twitter, LinkedIn и Google OAuth2 Sign-on .
Изучите ApplicationUserManager
класс , который содержит сведения об удостоверениях пользователей и настраивает следующие функции:
- Требования к надежности пароля.
- Блокировка пользователя (попытки и время).
- Двухфакторная проверка подлинности (2FA). Я рассмотрим 2FA и SMS в другом руководстве.
- Подключение служб электронной почты и SMS. (Я рассмотрим SMS в другом руководстве).
Класс ApplicationUserManager
является производным от универсального UserManager<ApplicationUser>
класса. ApplicationUser
является производным от IdentityUser. IdentityUser
является производным от универсального IdentityUser
класса:
// Default EntityFramework IUser implementation
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
{
public IdentityUser()
{
Claims = new List<TClaim>();
Roles = new List<TRole>();
Logins = new List<TLogin>();
}
/// User ID (Primary Key)
public virtual TKey Id { get; set; }
public virtual string Email { get; set; }
public virtual bool EmailConfirmed { get; set; }
public virtual string PasswordHash { get; set; }
/// A random value that should change whenever a users credentials have changed (password changed, login removed)
public virtual string SecurityStamp { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual bool PhoneNumberConfirmed { get; set; }
public virtual bool TwoFactorEnabled { get; set; }
/// DateTime in UTC when lockout ends, any time in the past is considered not locked out.
public virtual DateTime? LockoutEndDateUtc { get; set; }
public virtual bool LockoutEnabled { get; set; }
/// Used to record failures for the purposes of lockout
public virtual int AccessFailedCount { get; set; }
/// Navigation property for user roles
public virtual ICollection<TRole> Roles { get; private set; }
/// Navigation property for user claims
public virtual ICollection<TClaim> Claims { get; private set; }
/// Navigation property for user logins
public virtual ICollection<TLogin> Logins { get; private set; }
public virtual string UserName { get; set; }
}
Указанные выше свойства совпадают со свойствами в AspNetUsers
таблице, показанной выше.
Универсальные аргументы в IUser
позволяют создавать классы с использованием разных типов для первичного ключа. См. пример ChangePK , в котором показано, как изменить первичный ключ со строки на int или GUID.
ApplicationUser
ApplicationUser
(public class ApplicationUserManager : UserManager<ApplicationUser>
) определяется в Файле Models\IdentityModels.cs как:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in
// CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this,
DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
Выделенный выше код создает ClaimsIdentity. ASP.NET Identity и OWIN Cookie Authentication основаны на утверждениях, поэтому платформа требует, чтобы приложение создавало ClaimsIdentity
для пользователя. ClaimsIdentity
содержит сведения обо всех утверждениях пользователя, например об имени пользователя, возрасте и ролях, к которому принадлежит пользователь. На этом этапе можно также добавить дополнительные утверждения для пользователя.
Метод OWIN AuthenticationManager.SignIn
передает ClaimsIdentity
и выполняет вход пользователя:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties(){
IsPersistent = isPersistent },
await user.GenerateUserIdentityAsync(UserManager));
}
Приложение MVC 5 с Facebook, Twitter, LinkedIn и Google OAuth2 Sign-on показывает, как можно добавить дополнительные свойства в ApplicationUser
класс.
Подтверждение Email
Рекомендуется подтвердить сообщение электронной почты, по которому регистрируется новый пользователь, чтобы убедиться, что он не олицетворяет другого пользователя (т. е. он не зарегистрирован с чужой электронной почтой). Предположим, у вас есть форум для обсуждения, и вы хотите запретить "bob@example.com"
регистрацию в качестве "joe@contoso.com"
. Без подтверждения "joe@contoso.com"
сообщения электронной почты может получать нежелательные сообщения электронной почты из вашего приложения. Предположим, Что Боб случайно зарегистрировался как и не заметил этого, он не сможет использовать восстановление пароля, так как "bib@example.com"
приложение не имеет правильного адреса электронной почты. Email подтверждение обеспечивает только ограниченную защиту от ботов и не обеспечивает защиту от определенных спамеров, у них есть много рабочих псевдонимов электронной почты, которые они могут использовать для регистрации. В приведенном ниже примере пользователь не сможет изменить свой пароль до тех пор, пока его учетная запись не будет подтверждена (он выберет ссылку подтверждения, полученную в учетной записи электронной почты, с помощью которого он зарегистрировался). Вы можете применить этот рабочий процесс к другим сценариям, например к отправке ссылки для подтверждения и сброса пароля в новых учетных записях, созданных администратором, отправке пользователю сообщения электронной почты при изменении профиля и т. д. Как правило, вы хотите запретить новым пользователям публиковать какие-либо данные на веб-сайте, прежде чем они будут подтверждены электронной почтой, SMS-сообщением или другим механизмом.
Создание более полного примера
В этом разделе вы будете использовать NuGet для скачивания более полного примера, с которым мы будем работать.
Создайте пустой веб-проект ASP.NET.
В консоли диспетчера пакетов введите следующие команды:
Install-Package SendGrid Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
В этом руководстве мы будем использовать SendGrid для отправки электронной почты. Пакет
Identity.Samples
устанавливает код, с которым мы будем работать.Задайте для проекта использование SSL.
Протестируйте создание локальной учетной записи, запустив приложение, выбрав ссылку Регистрация и разместив форму регистрации.
Выберите демонстрационную ссылку электронной почты, которая имитирует подтверждение электронной почты.
Удалите код подтверждения ссылки на демонстрационную электронную почту из примера (код
ViewBag.Link
в контроллере учетной записи.DisplayEmail
См. методы действий иForgotPasswordConfirmation
представления razor .
Предупреждение
Если вы измените какие-либо параметры безопасности в этом примере, рабочим приложениям потребуется пройти аудит безопасности, который явно вызывает внесенные изменения.
Изучите код в App_Start\IdentityConfig.cs
В примере показано, как создать учетную запись и добавить ее в роль Администратор. Замените адрес электронной почты в примере на адрес электронной почты, который будет использоваться для учетной записи администратора. Сейчас самый простой способ создать учетную запись администратора — использовать метод программным способом Seed
. Мы надеемся, что в будущем появится средство, которое позволит создавать и администрировать пользователей и роли. Пример кода позволяет создавать пользователей и роли и управлять ими, но сначала необходимо иметь учетную запись администратора для запуска ролей и страниц администраторов пользователей. В этом примере учетная запись администратора создается при заполнения базы данных.
Измените пароль и измените имя на учетную запись, в которой можно получить Уведомления по электронной почте.
Предупреждение
Безопасность— никогда не сохраняйте конфиденциальные данные в исходном коде.
Как упоминалось ранее, app.CreatePerOwinContext
вызов в классе запуска добавляет обратные вызовы к методу Create
содержимого базы данных приложения, классам диспетчера пользователей и диспетчера ролей. Конвейер OWIN вызывает Create
метод для этих классов для каждого запроса и сохраняет контекст для каждого класса. Контроллер учетной записи предоставляет диспетчеру пользователей из контекста HTTP (который содержит контекст OWIN):
public ApplicationUserManager UserManager
{
get
{
return _userManager ??
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Когда пользователь регистрирует локальную учетную запись, HTTP Post 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)
{
var 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 this link: <a href=\""
+ callbackUrl + "\">link</a>");
// ViewBag.Link = callbackUrl; // Used only for initial demo.
return View("DisplayEmail");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
В приведенном выше коде используются данные модели для создания учетной записи пользователя с помощью введенного адреса электронной почты и пароля. Если псевдоним электронной почты находится в хранилище данных, создание учетной записи завершается сбоем и форма отображается снова. Метод GenerateEmailConfirmationTokenAsync
создает маркер безопасного подтверждения и сохраняет его в хранилище данных ASP.NET Identity. Метод Url.Action создает ссылку, UserId
содержащую маркер подтверждения и . Затем эта ссылка будет отправляться пользователю по электронной почте, и пользователь может выбрать ссылку в своем почтовом приложении, чтобы подтвердить свою учетную запись.
Настройка подтверждения по электронной почте
Перейдите на страницу регистрации SendGrid и зарегистрируйте бесплатную учетную запись. Добавьте код, аналогичный следующему, чтобы настроить SendGrid:
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
return configSendGridasync(message);
}
private 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)
{
return transportWeb.DeliverAsync(myMessage);
}
else
{
return Task.FromResult(0);
}
}
}
Примечание
Email клиенты часто принимают только текстовые сообщения (без HTML). Необходимо указать сообщение в текстовом формате и HTML. В приведенном выше примере SendGrid это делается с помощью кода и myMessage.Html
, показанного myMessage.Text
выше.
В следующем коде показано, как отправлять сообщения электронной почты с помощью класса MailMessage , где message.Body
возвращается только ссылка.
void sendMail(Message message)
{
#region formatter
string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";
html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
#endregion
MailMessage msg = new MailMessage();
msg.From = new MailAddress("joe@contoso.com");
msg.To.Add(new MailAddress(message.Destination));
msg.Subject = message.Subject;
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
SmtpClient smtpClient = new SmtpClient("smtp.gmail.com", Convert.ToInt32(587));
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("joe@contoso.com", "XXXXXX");
smtpClient.Credentials = credentials;
smtpClient.EnableSsl = true;
smtpClient.Send(msg);
}
Предупреждение
Безопасность— никогда не сохраняйте конфиденциальные данные в исходном коде. Учетная запись и учетные данные хранятся в appSetting. В Azure эти значения можно безопасно хранить на вкладке Настройка в портал Azure. Ознакомьтесь с рекомендациями по развертыванию паролей и других конфиденциальных данных в ASP.NET и Azure.
Введите учетные данные SendGrid, запустите приложение, зарегистрируйтесь с помощью псевдонима электронной почты, чтобы выбрать ссылку подтверждения в сообщении электронной почты. Сведения о том, как это сделать с помощью учетной записи электронной почты Outlook.com, см. в статье Настройка SMTP на C# для узла OUTLOOK.COM SMTP и егоASP.NET Identity 2.0: Настройка проверки учетной записи и авторизации Two-Factor .
После нажатия кнопки Зарегистрировать на его адрес электронной почты отправляется сообщение электронной почты с подтверждением, содержащее маркер проверки.
Пользователю отправляется сообщение электронной почты с маркером подтверждения для своей учетной записи.
Анализ кода
В следующем примере кода показан метод POST ForgotPassword
.
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");
}
var 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 here: <a href=\"" + callbackUrl + "\">link</a>");
return View("ForgotPasswordConfirmation");
}
// If we got this far, something failed, redisplay form
return View(model);
}
Если сообщение электронной почты пользователя не подтверждено, метод завершается автоматическим сбоем. Если ошибка была опубликована для недопустимого адреса электронной почты, злоумышленники могут использовать эти сведения, чтобы найти допустимый идентификатор пользователя (псевдонимы электронной почты) для атаки.
В следующем коде ConfirmEmail
показан метод в контроллере учетных записей, который вызывается, когда пользователь выбирает ссылку подтверждения в отправленном ему сообщении электронной почты:
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return View("ConfirmEmail");
}
AddErrors(result);
return View();
}
После использования маркера забытого пароля он делается недействительным. Следующее изменение кода в методе Create
(в файле App_Start\IdentityConfig.cs ) устанавливает срок действия маркеров в течение 3 часов.
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(3)
};
}
При использовании приведенного выше кода срок действия забытого пароля и маркеров подтверждения электронной почты истекает через 3 часа. Значение по умолчанию TokenLifespan
— один день.
В следующем коде показан метод подтверждения по электронной почте:
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
IdentityResult result;
try
{
result = await UserManager.ConfirmEmailAsync(userId, code);
}
catch (InvalidOperationException ioe)
{
// ConfirmEmailAsync throws when the userId is not found.
ViewBag.errorMessage = ioe.Message;
return View("Error");
}
if (result.Succeeded)
{
return View();
}
// If we got this far, something failed.
AddErrors(result);
ViewBag.errorMessage = "ConfirmEmail failed";
return View("Error");
}
Чтобы сделать приложение более безопасным, ASP.NET Identity поддерживает проверку подлинности Two-Factor (2FA). См . ASP.NET Identity 2.0: Настройка проверки учетной записи и Two-Factor авторизации От Джона Аттена. Хотя вы можете настроить блокировку учетной записи в случае неудачных попыток входа с паролем, такой подход делает ваше имя входа уязвимым к блокировке DOS . Мы рекомендуем использовать блокировку учетной записи только с 2FA.
Дополнительные ресурсы
- Обзор пользовательских поставщиков хранилищ для ASP.NET Identity
- Приложение MVC 5 с facebook, Twitter, LinkedIn и Google OAuth2 Sign-on также показывает, как добавить сведения профиля в таблицу пользователей.
- ASP.NET MVC и Identity 2.0: основные сведения о джоне Аттене.
- Введение в ASP.NET Identity
- Объявление о выпуске RTM ASP.NET Identity 2.0.0 от Pranav Rastogi.
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по