Двухфакторная проверка подлинности с помощью SMS и электронной почты в ASP.NET Identity
Хао Кунг (Hao Kung), Пранав Растоги (Pranav Rastogi),Рик Андерсон (Rick Anderson),Сухас Джоши (Suhas Joshi)
В этом руководстве показано, как настроить двухфакторную проверку подлинности (2FA) с помощью SMS и электронной почты.
Эта статья была написана Рик Андерсон (@RickAndMSFT), Пранав Растоги (@rustd), Хао Кунг и Сухас Джоши. Пример NuGet был написан в основном Хао Кунгом.
В этой статье рассматриваются следующие вопросы:
- Создание примера удостоверения
- Настройка SMS для двухфакторной проверки подлинности
- Включение двухфакторной проверки подлинности
- Регистрация поставщика двухфакторной проверки подлинности
- Объединение учетных записей для входа в социальных сетях и локальных учетных записей
- Блокировка учетной записи от атак методом подбора
Создание примера удостоверения
В этом разделе вы будете использовать NuGet для скачивания примера, с которым мы будем работать. Начните с установки и запуска Visual Studio Express 2013 для Web или Visual Studio 2013. Установите Visual Studio 2013 с обновлением 2 или более поздней версии.
Примечание
Предупреждение. Для работы с этим руководством необходимо установить Visual Studio 2013 с обновлением 2 .
Создайте пустой веб-проект ASP.NET.
В консоли диспетчера пакетов введите следующие команды:
Install-Package SendGrid
Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
В этом руководстве мы будем использовать SendGrid для отправки электронной почты и Twilio или ASPSMS для отправки текстовых сообщений. Пакет
Identity.Samples
устанавливает код, с которым мы будем работать.Задайте для проекта использование SSL.
Необязательно. Следуйте инструкциям в руководстве по подтверждению Email, чтобы подключить SendGrid, а затем запустить приложение и зарегистрировать учетную запись электронной почты.
Дополнительные: Удалите код подтверждения демонстрационной ссылки электронной почты из примера (код
ViewBag.Link
в контроллере учетной записи. См.DisplayEmail
методы действия иForgotPasswordConfirmation
представления Razor .Дополнительные:
ViewBag.Status
Удалите код из контроллеров управления и учетных записей, а также из представлений Razor Views\Account\VerifyCode.cshtml и Views\Manage\VerifyPhoneNumber.cshtml . Кроме того, вы можете сохранитьViewBag.Status
дисплей, чтобы проверить, как это приложение работает локально без необходимости подключения и отправки сообщений электронной почты и SMS.
Примечание
Предупреждение. Если изменить какой-либо из параметров безопасности в этом примере, рабочим приложениям потребуется пройти аудит безопасности, который явно вызывает внесенные изменения.
Настройка SMS для двухфакторной проверки подлинности
В этом руководстве содержатся инструкции по использованию Twilio или ASPSMS, но вы можете использовать любой другой поставщик SMS.
Создание учетной записи пользователя с помощью поставщика SMS
Установка дополнительных пакетов или добавление ссылок на службы
Twilio:
В консоли диспетчера пакетов введите следующую команду.
Install-Package Twilio
ASPSMS:
Необходимо добавить следующую ссылку на службу:Адрес:
https://webservice.aspsms.com/aspsmsx2.asmx?WSDL
Пространство имен:
ASPSMSX2
Определение учетных данных пользователя поставщика SMS
Twilio:
На вкладке Панель мониторинга учетной записи Twilio скопируйте идентификатор безопасности учетной записи и маркер проверки подлинности.ASPSMS:
В параметрах учетной записи перейдите в раздел Userkey и скопируйте его вместе со своим самоопределитым паролем.Позже эти значения будут сохранены в переменных
SMSAccountIdentification
иSMSAccountPassword
.Указание SenderID или инициатора
Twilio:
На вкладке Числа скопируйте свой номер телефона Twilio.ASPSMS:
В меню Разблокировать источники разблокируйте один или несколько источников или выберите буквенно-цифровой инициатор (не поддерживается всеми сетями).Позже мы будем хранить это значение в переменной
SMSAccountFrom
.Передача учетных данных поставщика SMS в приложение
Сделайте учетные данные и номер телефона отправителя доступными для приложения:
public static class Keys { public static string SMSAccountIdentification = "My Idenfitication"; public static string SMSAccountPassword = "My Password"; public static string SMSAccountFrom = "+15555551234"; }
Предупреждение
Безопасность. Никогда не храните конфиденциальные данные в исходном коде. Учетная запись и учетные данные добавляются в приведенный выше код, чтобы упростить пример. См. раздел ASP.NET MVC Джона Аттена: сохранение закрытых параметров из системы управления версиями.
Реализация передачи данных поставщику SMS
SmsService
Настройте класс в файле App_Start\IdentityConfig.cs.В зависимости от используемого поставщика SMS активируйте раздел Twilio или ASPSMS :
public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Twilio Begin // var Twilio = new TwilioRestClient( // Keys.SMSAccountIdentification, // Keys.SMSAccountPassword); // var result = Twilio.SendMessage( // Keys.SMSAccountFrom, // message.Destination, message.Body // ); // Status is one of Queued, Sending, Sent, Failed or null if the number is not valid // Trace.TraceInformation(result.Status); // Twilio doesn't currently have an async API, so return success. // return Task.FromResult(0); // Twilio End // ASPSMS Begin // var soapSms = new WebApplication1.ASPSMSX2.ASPSMSX2SoapClient("ASPSMSX2Soap"); // soapSms.SendSimpleTextSMS( // Keys.SMSAccountIdentification, // Keys.SMSAccountPassword, // message.Destination, // Keys.SMSAccountFrom, // message.Body); // soapSms.Close(); // return Task.FromResult(0); // ASPSMS End } }
Запустите приложение и войдите в систему с ранее зарегистрированной учетной записью.
Щелкните идентификатор пользователя, который активирует
Index
метод действия вManage
контроллере.Нажмите кнопку "Добавить".
Через несколько секунд вы получите текстовое сообщение с кодом проверки. Введите его и нажмите кнопку Отправить.
В представлении Управление отображается добавленный номер телефона.
Анализ кода
// GET: /Account/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two factor provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "The phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(User.Identity.GetUserId()),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(User.Identity.GetUserId()),
Logins = await UserManager.GetLoginsAsync(User.Identity.GetUserId()),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId())
};
return View(model);
}
Метод Index
действия в Manage
контроллере задает сообщение о состоянии на основе предыдущего действия и предоставляет ссылки для изменения локального пароля или добавления локальной учетной записи. Метод Index
также отображает состояние или номер телефона 2FA, внешние имена входа, включенную двухфакторную проверку подлинности и метод 2FA для этого браузера (описано далее). Если щелкнуть идентификатор пользователя (адрес электронной почты) в строке заголовка, сообщение не передается. При щелчке по ссылке Номер телефона : удалить передается Message=RemovePhoneSuccess
строка запроса.
https://localhost:44300/Manage?Message=RemovePhoneSuccess
[]
Метод AddPhoneNumber
действия отображает диалоговое окно для ввода номера телефона, который может принимать SMS-сообщения.
// GET: /Account/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
При нажатии кнопки Отправить код проверки номер телефона отправляется в метод действия HTTP POST AddPhoneNumber
.
// POST: /Account/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
User.Identity.GetUserId(), model.Number);
if (UserManager.SmsService != null)
{
var message = new IdentityMessage
{
Destination = model.Number,
Body = "Your security code is: " + code
};
// Send token
await UserManager.SmsService.SendAsync(message);
}
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}
Метод GenerateChangePhoneNumberTokenAsync
создает маркер безопасности, который будет задан в SMS-сообщении. Если служба SMS настроена, маркер отправляется в виде строки "Ваш код безопасности является <токеном>". Метод SmsService.SendAsync
для вызывается асинхронно, а затем приложение перенаправляется в VerifyPhoneNumber
метод действия (в котором отображается следующее диалоговое окно), где можно ввести код проверки.
После ввода кода и нажатия кнопки Отправить код отправляется в метод действия HTTP POST VerifyPhoneNumber
.
// POST: /Account/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInAsync(user, isPersistent: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "Failed to verify phone");
return View(model);
}
Метод ChangePhoneNumberAsync
проверяет опубликованный код безопасности. Если код правильный, номер телефона добавляется в PhoneNumber
поле AspNetUsers
таблицы. Если этот вызов выполнен успешно, SignInAsync
вызывается метод :
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
// Clear the temporary cookies used for external and two factor sign ins
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.TwoFactorCookie);
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = isPersistent
},
await user.GenerateUserIdentityAsync(UserManager));
}
Параметр isPersistent
задает, сохраняется ли сеанс проверки подлинности в нескольких запросах.
При изменении профиля безопасности создается новая метка безопасности и сохраняется в SecurityStamp
поле таблицы AspNetUsers . Обратите внимание, что SecurityStamp
поле отличается от файла cookie безопасности. Файл cookie безопасности не хранится в AspNetUsers
таблице (или где-либо еще в базе данных удостоверений). Маркер cookie безопасности самозаверяется с помощью DPAPI и создается с UserId, SecurityStamp
информацией о времени окончания срока действия и .
ПО промежуточного слоя для файлов cookie проверяет файл cookie при каждом запросе. Метод SecurityStampValidator
в классе попадает в Startup
базу данных и периодически проверяет метку безопасности, как указано в validateInterval
. Это происходит каждые 30 минут (в нашем примере), если вы не измените профиль безопасности. Был выбран 30-минутный интервал, чтобы свести к минимуму количество обращений к базе данных.
Метод SignInAsync
необходимо вызывать при изменении профиля безопасности. При изменении профиля безопасности база данных обновляет SecurityStamp
поле, и без вызова SignInAsync
метода вы будете оставаться в системе только до следующего попадания конвейера OWIN в базу данных ().validateInterval
Это можно проверить, изменив SignInAsync
метод для немедленного возврата и задав свойство cookie validateInterval
с 30 минут на 5 секунд:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
return;
// Clear any partial cookies from external or two factor partial sign ins
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.TwoFactorCookie);
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = isPersistent
},
await user.GenerateUserIdentityAsync(UserManager));
}
public void ConfigureAuth(IAppBuilder app) {
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a
// third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add
// an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
//validateInterval: TimeSpan.FromMinutes(30),
validateInterval: TimeSpan.FromSeconds(5),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
С помощью приведенных выше изменений кода можно изменить профиль безопасности (например, изменив состояние Two Factor Enabled) и выйти из системы через 5 секунд при сбое SecurityStampValidator.OnValidateIdentity
метода. Удалите строку возврата в методе SignInAsync
, внесите другое изменение профиля безопасности, и вы не будете выйдут из системы. Метод SignInAsync
создает новый файл cookie безопасности.
Включение двухфакторной проверки подлинности
В примере приложения необходимо использовать пользовательский интерфейс для включения двухфакторной проверки подлинности (2FA). Чтобы включить 2FA, щелкните свой идентификатор пользователя (псевдоним электронной почты) на панели навигации.
Щелкните Включить 2FA. Выйдите из системы, а затем снова войдите в систему. Если вы включили электронную почту (см. мой предыдущий учебник), вы можете выбрать SMS или электронную почту для 2FA. Отобразится страница Проверка кода, где можно ввести код (из SMS или электронной почты). Если щелкнуть поле Запомнить этот браузер проверка, вам не потребуется использовать 2FA для входа с помощью этого компьютера и браузера. Включение 2FA и нажатие кнопки Запомнить этот браузер обеспечит надежную защиту 2FA от злоумышленников, пытающихся получить доступ к вашей учетной записи, если у них нет доступа к вашему компьютеру. Это можно сделать на любом частном компьютере, который вы регулярно используете. Установив параметр Запомнить этот браузер, вы получаете дополнительную защиту двухфаковых параметров на компьютерах, которые вы не используете регулярно, и получаете удобство, не проходя через 2FA на собственных компьютерах.
Регистрация поставщика двухфакторной проверки подлинности
При создании проекта MVC файл IdentityConfig.cs содержит следующий код для регистрации поставщика двухфакторной проверки подлинности:
public static ApplicationUserManager Create(
IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
var manager = new ApplicationUserManager(
new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Register two factor authentication providers. This application uses Phone and Emails as a
// step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is: {0}"
});
manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is: {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
Добавление номера телефона для 2FA
Метод AddPhoneNumber
действия в контроллере Manage
создает маркер безопасности и отправляет его на указанный вами номер телефона.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token and send it
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(
User.Identity.GetUserId(), model.Number);
if (UserManager.SmsService != null)
{
var message = new IdentityMessage
{
Destination = model.Number,
Body = "Your security code is: " + code
};
await UserManager.SmsService.SendAsync(message);
}
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}
После отправки маркер перенаправляется в VerifyPhoneNumber
метод действия, где можно ввести код для регистрации SMS для 2FA. Sms 2FA не используется, пока вы не проверите номер телефона.
Включение 2FA
Метод EnableTFA
действия включает 2FA:
// POST: /Manage/EnableTFA
[HttpPost]
public async Task<ActionResult> EnableTFA()
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInAsync(user, isPersistent: false);
}
return RedirectToAction("Index", "Manage");
}
Обратите внимание, SignInAsync
что должен вызываться , так как включение 2FA является изменением профиля безопасности. Если 2FA включена, пользователю потребуется использовать 2FA для входа, используя зарегистрированные подходы к 2FA (SMS и электронная почта в примере).
Вы можете добавить дополнительные поставщики 2FA, такие как генераторы QR-кода, или написать собственные.
Примечание
Коды 2FA создаются с помощью алгоритма одноразового пароля на основе времени , а коды действительны в течение шести минут. Если вы введете код более шести минут, вы получите сообщение об ошибке Недопустимый код.
Объединение учетных записей для входа в социальных сетях и локальных учетных записей
Вы можете объединить локальные учетные записи и учетные записи социальных параметров, щелкнув ссылку электронной почты. В следующей последовательности сначала создается локальноеRickAndMSFT@gmail.com имя входа, но вы можете сначала создать учетную запись как социальный вход, а затем добавить локальное имя входа.
Щелкните ссылку Управление . Обратите внимание на 0 внешних (учетных записей социальных параметров), связанных с этой учетной записью.
Щелкните ссылку на другую службу входа и примите запросы приложения. Две учетные записи были объединены, и вы сможете войти в систему с помощью любой из них. Вы можете захотеть, чтобы пользователи добавляли локальные учетные записи в случае, если служба проверки подлинности в социальных сетях не работает или, скорее всего, они потеряли доступ к своей учетной записи социальной сети.
На следующем изображении Tom — это вход в социальную сеть (который можно увидеть на странице Внешние имена входа: 1 ).
Щелкнув Выбрать пароль , вы можете добавить локальный вход, связанный с той же учетной записью.
Блокировка учетной записи от атак методом подбора
Вы можете защитить учетные записи в приложении от атак по словарю, включив блокировку пользователей. Следующий код в методе ApplicationUserManager Create
настраивает блокировку:
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
Приведенный выше код включает блокировку только для двухфакторной проверки подлинности. Хотя вы можете включить блокировку для имен входа, изменив shouldLockout
значение true в Login
методе контроллера учетной записи, рекомендуется не включать блокировку для имен входа, так как это делает учетную запись уязвимой для атак на вход DOS . В примере кода блокировка отключена для учетной записи администратора, созданной в методе ApplicationDbInitializer Seed
:
public static void InitializeIdentityForEF(ApplicationDbContext db)
{
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin@example.com";
const string roleName = "Admin";
//Create Role Admin if it does not exist
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null)
{
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, GetSecurePassword());
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
}
Требование наличия у пользователя проверенной учетной записи электронной почты
Следующий код требует, чтобы у пользователя была проверенная учетная запись электронной почты, прежде чем он сможет войти в систему:
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 doen't count login failures towards lockout only two factor authentication
// To enable password failures to trigger 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 });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Как SignInManager проверяет наличие требований к 2FA
Локальный вход и социальный вход в проверка, чтобы узнать, включена ли 2FA. Если включена 2FA, SignInManager
метод входа возвращает SignInStatus.RequiresVerification
, и пользователь будет перенаправлен на SendCode
метод действия, где ей потребуется ввести код для завершения последовательности входа. Если для локального файла cookie пользователя задан параметр RememberMe, возвращается SignInStatus.Success
, SignInManager
и ей не придется проходить через 2FA.
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 doen't count login failures towards lockout only two factor authentication
// To enable password failures to trigger 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 });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
В следующем коде SendCode
показан метод действия. Создается элемент SelectListItem со всеми методами 2FA, включенными для пользователя. SelectListItem передается вспомогательной функции DropDownListFor, которая позволяет пользователю выбрать подход 2FA (обычно это электронная почта и SMS).
public async Task<ActionResult> SendCode(string returnUrl)
{
var userId = await SignInManager.GetVerifiedUserIdAsync();
if (userId == null)
{
return View("Error");
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl });
}
После того как пользователь публикует подход 2FA, HTTP POST SendCode
вызывается метод действия, SignInManager
отправляет код 2FA, а пользователь перенаправляется в VerifyCode
метод действия, где он может ввести код для завершения входа.
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SendCode(SendCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View();
}
// Generate the token and send it
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
{
return View("Error");
}
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl });
}
Блокировка 2FA
Хотя вы можете настроить блокировку учетной записи в случае неудачных попыток входа с паролем, такой подход делает ваше имя входа уязвимым к блокировке DOS . Мы рекомендуем использовать блокировку учетной записи только с 2FA. ApplicationUserManager
При создании в примере кода устанавливается блокировка 2FA и MaxFailedAccessAttemptsBeforeLockout
значение 5. После входа пользователя (с помощью локальной учетной записи или учетной записи социальной сети) сохраняется каждая неудачная попытка 2FA, и если достигнуто максимальное количество попыток, пользователь блокируется на пять минут (вы можете задать время блокировки с помощью DefaultAccountLockoutTimeSpan
).
Дополнительные ресурсы
- Рекомендуемые ресурсы ASP.NET Identity Полный список блогов, видео, учебников и отличных ссылок so.
- Приложение MVC 5 с facebook, Twitter, LinkedIn и Google OAuth2 Sign-on также показывает, как добавить сведения профиля в таблицу пользователей.
- ASP.NET MVC и Identity 2.0: основные сведения о джоне Аттене.
- Подтверждение учетной записи и восстановление пароля с помощью удостоверения ASP.NET
- Введение в ASP.NET Identity
- Объявление о выпуске RTM ASP.NET Identity 2.0.0 от Pranav Rastogi.
- ASP.NET Identity 2.0: настройка проверки учетной записи и авторизации Two-Factor от Джона Аттена.