Uwierzytelnianie dwuskładnikowe za pomocą wiadomości SMS i wiadomości e-mail w systemie ASP.NET Identity
Przez Hao Kung, Pranav Rastogi, Rick Anderson, Suhas Joshi
W tym samouczku pokazano, jak skonfigurować uwierzytelnianie dwuskładnikowe (2FA) przy użyciu wiadomości SMS i poczty e-mail.
Ten artykuł został napisany przez Ricka Andersona (@RickAndMSFT), Pranav Rastogi (@rustd), Hao Kung i Suhas Joshi. Przykład NuGet został napisany głównie przez Hao Kung.
W tym temacie omówiono następujące kwestie:
- Kompilowanie przykładu Identity
- Konfigurowanie programu SMS na potrzeby uwierzytelniania dwuskładnikowego
- Włączanie uwierzytelniania dwuskładnikowego
- Jak zarejestrować dostawcę uwierzytelniania dwuskładnikowego
- Łączenie kont logowania społecznościowego i lokalnego
- Blokada konta przed atakami siłowymi
Kompilowanie przykładu Identity
W tej sekcji użyjesz narzędzia NuGet, aby pobrać przykład, z którym będziemy pracować. Rozpocznij od zainstalowania i uruchomienia Visual Studio Express 2013 dla sieci Web lub Visual Studio 2013. Zainstaluj program Visual Studio 2013 Update 2 lub nowszy.
Uwaga
Ostrzeżenie: Aby ukończyć ten samouczek, musisz zainstalować program Visual Studio 2013 Update 2 .
Utwórz nowy pusty projekt ASP.NET sieci Web.
W konsoli Menedżera pakietów wprowadź następujące polecenia:
Install-Package SendGrid
Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
W tym samouczku użyjemy usługi SendGrid do wysyłania wiadomości e-mail i usługi Twilio lub ASPSMS na potrzeby sms-ów. Pakiet
Identity.Samples
instaluje kod, z którym będziemy pracować.Opcjonalnie: postępuj zgodnie z instrukcjami w samouczku z potwierdzeniem Email, aby podłączyć usługę SendGrid, a następnie uruchomić aplikację i zarejestrować konto e-mail.
Opcjonalne: Usuń kod potwierdzenia linku demonstracyjnego wiadomości e-mail z przykładu (
ViewBag.Link
kod w kontrolerze konta.DisplayEmail
Zobacz metody iForgotPasswordConfirmation
akcji i widoki razor ).Opcjonalne:
ViewBag.Status
Usuń kod z kontrolerów zarządzanie i konta oraz z widoków Views\Account\VerifyCode.cshtml i Views\Manage\VerifyPhoneNumber.cshtml razor views. Możesz też zachowaćViewBag.Status
wyświetlanie, aby przetestować sposób działania tej aplikacji lokalnie bez konieczności podłączania i wysyłania wiadomości e-mail i wiadomości SMS.
Uwaga
Ostrzeżenie: Jeśli zmienisz dowolne ustawienia zabezpieczeń w tym przykładzie, aplikacje produkcyjne będą musiały przejść inspekcję zabezpieczeń, która jawnie wywołuje wprowadzone zmiany.
Konfigurowanie programu SMS na potrzeby uwierzytelniania dwuskładnikowego
Ten samouczek zawiera instrukcje dotyczące korzystania z usługi Twilio lub ASPSMS, ale możesz użyć dowolnego innego dostawcy programu SMS.
Tworzenie konta użytkownika za pomocą dostawcy programu SMS
Utwórz konto usługi Twilio lub asPSMS .
Instalowanie dodatkowych pakietów lub dodawanie odwołań do usługi
Twilio:
W konsoli Menedżera pakietów wprowadź następujące polecenie:
Install-Package Twilio
ASPSMS:
Należy dodać następujące odwołanie do usługi:Adres:
https://webservice.aspsms.com/aspsmsx2.asmx?WSDL
Przestrzeń nazw:
ASPSMSX2
Określanie poświadczeń użytkownika dostawcy programu SMS
Twilio:
Na karcie Pulpit nawigacyjny konta usługi Twilio skopiuj identyfikator SID konta i token uwierzytelniania.ASPSMS:
W ustawieniach konta przejdź do pozycji Userkey i skopiuj je wraz z własnym hasłem.Te wartości zostaną później przechowywane w zmiennych
SMSAccountIdentification
iSMSAccountPassword
.Określanie identyfikatora nadawcy/elementu źródłowego
Twilio:
Na karcie Numery skopiuj swój numer telefonu usługi Twilio.ASPSMS:
W menu Odblokuj elementy źródłowe odblokuj co najmniej jedną jednostkę źródłową lub wybierz alfanumeryczną jednostkę źródłową (nieobsługiwana przez wszystkie sieci).Później zapiszemy tę wartość w zmiennej
SMSAccountFrom
.Przenoszenie poświadczeń dostawcy programu SMS do aplikacji
Podaj poświadczenia i numer telefonu nadawcy w aplikacji:
public static class Keys { public static string SMSAccountIdentification = "My Idenfitication"; public static string SMSAccountPassword = "My Password"; public static string SMSAccountFrom = "+15555551234"; }
Ostrzeżenie
Zabezpieczenia — nigdy nie przechowuj poufnych danych w kodzie źródłowym. Konto i poświadczenia są dodawane do powyższego kodu, aby zachować prosty przykład. Zobacz ASP.NET MVC Jona Attena : Zachowaj ustawienia prywatne poza kontrolą źródła.
Implementacja transferu danych do dostawcy programu SMS
Skonfiguruj klasę
SmsService
w pliku App_Start\IdentityConfig.cs .W zależności od używanego dostawcy programu SMS aktywuj usługę Twilio lub sekcję 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 } }
Uruchom aplikację i zaloguj się przy użyciu wcześniej zarejestrowanego konta.
Kliknij swój identyfikator użytkownika, który aktywuje metodę
Index
akcji wManage
kontrolerze.Kliknij pozycję Add (Dodaj).
W ciągu kilku sekund otrzymasz wiadomość SMS z kodem weryfikacyjnym. Wprowadź go i naciśnij przycisk Prześlij.
Widok Zarządzaj pokazuje, że numer telefonu został dodany.
Analizowanie kodu
// 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);
}
Metoda Index
akcji w Manage
kontrolerze ustawia komunikat o stanie na podstawie poprzedniej akcji i udostępnia linki do zmiany hasła lokalnego lub dodania konta lokalnego. Metoda Index
wyświetla również stan lub numer telefonu 2FA, identyfikatory logowania zewnętrzne, włączoną uwierzytelnianie 2FA i zapamiętaj metodę 2FA dla tej przeglądarki (wyjaśnij później). Kliknięcie identyfikatora użytkownika (e-mail) na pasku tytułu nie przekazuje wiadomości. Kliknięcie linku Numer telefonu: usuń przekazuje Message=RemovePhoneSuccess
link jako ciąg zapytania.
https://localhost:44300/Manage?Message=RemovePhoneSuccess
[]
Metoda AddPhoneNumber
akcji wyświetla okno dialogowe, aby wprowadzić numer telefonu, który może odbierać wiadomości SMS.
// GET: /Account/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
Kliknięcie przycisku Wyślij kod weryfikacyjny powoduje opublikowanie numeru telefonu do metody akcji 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 });
}
Metoda GenerateChangePhoneNumberTokenAsync
generuje token zabezpieczający, który zostanie ustawiony w wiadomości SMS. Jeśli usługa SMS została skonfigurowana, token jest wysyłany jako ciąg "Twój kod zabezpieczeń jest <tokenem>". SmsService.SendAsync
Metoda do jest wywoływana asynchronicznie, a następnie aplikacja jest przekierowywana do VerifyPhoneNumber
metody akcji (która wyświetla następujące okno dialogowe), gdzie można wprowadzić kod weryfikacyjny.
Po wprowadzeniu kodu i kliknięciu przycisku prześlij kod zostanie opublikowany w metodzie akcji 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);
}
Metoda ChangePhoneNumberAsync
sprawdza opublikowany kod zabezpieczeń. Jeśli kod jest poprawny, numer telefonu jest dodawany do PhoneNumber
pola AspNetUsers
tabeli. Jeśli to wywołanie powiedzie się, metoda jest wywoływana 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));
}
Parametr określa, czy sesja isPersistent
uwierzytelniania jest utrwalana w wielu żądaniach.
Po zmianie profilu zabezpieczeń zostanie wygenerowana nowa sygnatura zabezpieczeń i zapisana w SecurityStamp
polu tabeli AspNetUsers . Uwaga: SecurityStamp
pole różni się od pliku cookie zabezpieczeń. Plik cookie zabezpieczeń nie jest przechowywany w AspNetUsers
tabeli (ani nigdzie indziej w bazie danych tożsamości). Token pliku cookie zabezpieczeń jest podpisany samodzielnie przy użyciu interfejsu UserId, SecurityStamp
DPAPI i jest tworzony przy użyciu informacji o czasie wygaśnięcia i .
Oprogramowanie pośredniczące pliku cookie sprawdza plik cookie na każdym żądaniu. Metoda SecurityStampValidator
w Startup
klasie okresowo sprawdza bazę danych i sprawdza sygnaturę zabezpieczeń zgodnie z parametrem validateInterval
. Dzieje się to tylko co 30 minut (w naszym przykładzie), chyba że zmienisz profil zabezpieczeń. Wybrano 30-minutowy interwał, aby zminimalizować podróże do bazy danych.
Metoda SignInAsync
musi zostać wywołana po wprowadzeniu dowolnej zmiany w profilu zabezpieczeń. Gdy profil zabezpieczeń ulegnie zmianie, baza danych zostanie zaktualizowana SecurityStamp
w polu i bez wywołania SignInAsync
metody zostanie zalogowana tylko do momentu następnego trafienia potoku OWIN do bazy danych ().validateInterval
Można to przetestować, zmieniając metodę SignInAsync
, aby zwracać natychmiast, i ustawiając właściwość pliku cookie validateInterval
z 30 minut na 5 sekund:
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);
Po wprowadzeniu powyższych zmian w kodzie można zmienić profil zabezpieczeń (na przykład przez zmianę stanu Włączone dwa czynniki) i wylogować się w ciągu 5 sekund, gdy SecurityStampValidator.OnValidateIdentity
metoda zakończy się niepowodzeniem. Usuń wiersz zwrotny w metodzie SignInAsync
, wprowadź inną zmianę profilu zabezpieczeń i nie zostanie wylogowany. Metoda SignInAsync
generuje nowy plik cookie zabezpieczeń.
Włącz uwierzytelnianie dwuskładnikowe
W przykładowej aplikacji musisz użyć interfejsu użytkownika, aby włączyć uwierzytelnianie dwuskładnikowe (2FA). Aby włączyć uwierzytelnianie 2FA, kliknij swój identyfikator użytkownika (alias adresu e-mail) na pasku nawigacyjnym.
Kliknij pozycję Włącz uwierzytelnianie 2FA. Wyloguj się, a następnie zaloguj się ponownie. Jeśli włączono pocztę e-mail (zobacz mój poprzedni samouczek), możesz wybrać wiadomość SMS lub wiadomość e-mail dla uwierzytelniania 2FA. Zostanie wyświetlona strona Weryfikuj kod, na której można wprowadzić kod (z wiadomości SMS lub wiadomości e-mail). Kliknięcie pola wyboru Zapamiętaj tę przeglądarkę spowoduje wykluczenie konieczności użycia uwierzytelniania 2FA do zalogowania się na tym komputerze i w przeglądarce. Włączenie uwierzytelniania 2FA i kliknięcie przycisku Zapamiętaj tę przeglądarkę zapewni silną ochronę 2FA przed złośliwymi użytkownikami próbującymi uzyskać dostęp do konta, o ile nie mają dostępu do komputera. Można to zrobić na dowolnej maszynie prywatnej, której regularnie używasz. Ustawiając ustawienie Zapamiętaj tę przeglądarkę, uzyskujesz dodatkowe zabezpieczenia uwierzytelniania 2FA z komputerów, które nie są regularnie używane, i wygoda w przypadku braku konieczności przechodzenia przez uwierzytelnianie 2FA na własnych komputerach.
Jak zarejestrować dostawcę uwierzytelniania dwuskładnikowego
Podczas tworzenia nowego projektu MVC plik IdentityConfig.cs zawiera następujący kod umożliwiający zarejestrowanie dostawcy uwierzytelniania dwuskładnikowego:
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;
}
Dodawanie numeru telefonu dla uwierzytelniania 2FA
AddPhoneNumber
Metoda akcji w kontrolerze Manage
generuje token zabezpieczający i wysyła go do podanego numeru telefonu.
[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 });
}
Po wysłaniu tokenu nastąpi przekierowanie do VerifyPhoneNumber
metody akcji, w której można wprowadzić kod w celu zarejestrowania wiadomości SMS na potrzeby 2FA. Uwierzytelnianie SMS 2FA nie jest używane do momentu zweryfikowania numeru telefonu.
Włączanie uwierzytelniania 2FA
EnableTFA
Metoda akcji włącza uwierzytelnianie 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");
}
Należy pamiętać, że element musi być wywoływany SignInAsync
, ponieważ włączenie uwierzytelniania 2FA jest zmianą profilu zabezpieczeń. Po włączeniu uwierzytelniania 2FA użytkownik będzie musiał zalogować się przy użyciu uwierzytelniania 2FA, korzystając z metod uwierzytelniania 2FA zarejestrowanych (wiadomości SMS i wiadomości e-mail w przykładzie).
Możesz dodać więcej dostawców uwierzytelniania 2FA, takich jak generatory kodu QR, lub napisać własne.
Uwaga
Kody 2FA są generowane przy użyciu jednorazowego algorytmu haseł opartego na czasie , a kody są ważne przez sześć minut. Jeśli wprowadzenie kodu potrwa dłużej niż sześć minut, zostanie wyświetlony komunikat o błędzie Nieprawidłowy kod.
Łączenie kont logowania społecznościowego i lokalnego
Możesz połączyć konta lokalne i społecznościowe, klikając link do poczty e-mail. W poniższej sekwencji "RickAndMSFT@gmail.com" najpierw jest tworzony jako identyfikator logowania lokalnego, ale najpierw możesz utworzyć konto jako dziennik społecznościowy, a następnie dodać lokalne dane logowania.
Kliknij link Zarządzaj . Zwróć uwagę na 0 zewnętrznych (identyfikatorów logowania społecznościowego) skojarzonych z tym kontem.
Kliknij link do innej usługi logowania i zaakceptuj żądania aplikacji. Te dwa konta zostały połączone. Możesz zalogować się przy użyciu jednego z tych kont. Możesz chcieć, aby użytkownicy dodawali konta lokalne na wypadek awarii usługi uwierzytelniania w sieci społecznościowej lub prawdopodobnie utracili dostęp do konta społecznościowego.
Na poniższej ilustracji Tom jest dziennikiem społecznościowym (widocznym na stronie są dane logowania zewnętrzne: 1 ).
Kliknięcie pozycji Wybierz hasło umożliwia dodanie lokalnego logowania skojarzonego z tym samym kontem.
Blokada konta przed atakami siłowymi
Konta w aplikacji można chronić przed atakami słownika, włączając blokadę użytkownika. Poniższy kod w metodzie ApplicationUserManager Create
konfiguruje blokadę:
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
Powyższy kod włącza blokadę tylko dla uwierzytelniania dwuskładnikowego. Chociaż możesz włączyć blokadę dla logowań, zmieniając wartość shouldLockout
na true w Login
metodzie kontrolera konta, zalecamy, aby nie włączać blokady dla logowań, ponieważ powoduje to, że konto jest podatne na ataki logowania do systemu DOS . W przykładowym kodzie blokada jest wyłączona dla konta administratora utworzonego w metodzie 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);
}
}
Wymaganie od użytkownika posiadania zweryfikowanego konta e-mail
Poniższy kod wymaga, aby użytkownik miał zweryfikowane konto e-mail, zanim będzie mógł się zalogować:
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);
}
}
Jak narzędzie SignInManager sprawdza wymaganie uwierzytelniania 2FA
Zarówno logowanie lokalne, jak i logowanie społecznościowe są sprawdzane, czy włączono uwierzytelnianie 2FA. Jeśli uwierzytelnianie 2FA jest włączone, SignInManager
metoda logowania zwraca SignInStatus.RequiresVerification
wartość , a użytkownik zostanie przekierowany do SendCode
metody akcji, gdzie będzie musiał wprowadzić kod w celu ukończenia logowania w sekwencji. Jeśli użytkownik ma ustawienie RememberMe na lokalnym pliku cookie użytkowników, zostanie zwrócony SignInManager
SignInStatus.Success
i nie będzie musiał przechodzić przez 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 });
}
}
Poniższy kod przedstawia metodę SendCode
akcji. Element SelectListItem jest tworzony ze wszystkimi metodami 2FA włączonymi dla użytkownika. Element SelectListItem jest przekazywany do pomocnika DropDownListFor , który umożliwia użytkownikowi wybranie podejścia 2FA (zazwyczaj wiadomości e-mail i wiadomości 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 });
}
Gdy użytkownik opublikuje metodę 2FA, wywoływana jest metoda akcji, SignInManager
wysyła kod 2FA, a użytkownik jest przekierowywany do VerifyCode
metody akcji, HTTP POST SendCode
w której może wprowadzić kod w celu ukończenia logowania.
//
// 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 });
}
Blokada 2FA
Mimo że można ustawić blokadę konta w przypadku niepowodzeń prób logowania, takie podejście sprawia, że logowanie jest podatne na blokady systemu DOS . Zalecamy używanie blokady konta tylko z uwierzytelnianiem 2FA. Po utworzeniu ApplicationUserManager
przykładowy kod ustawia blokadę 2FA i MaxFailedAccessAttemptsBeforeLockout
na pięć. Po zalogowaniu się użytkownika (za pośrednictwem konta lokalnego lub konta społecznościowego) każda nieudana próba 2FA jest przechowywana i jeśli zostanie osiągnięta maksymalna liczba prób, użytkownik zostanie zablokowany przez pięć minut (możesz ustawić czas blokady za DefaultAccountLockoutTimeSpan
pomocą polecenia ).
Dodatkowe zasoby
- zalecane zasoby dotyczące tożsamości ASP.NET Pełna lista blogów tożsamości, filmów wideo, samouczków i doskonałych linków SO.
- Aplikacja MVC 5 z usługami Facebook, Twitter, LinkedIn i Google OAuth2 Sign-on pokazuje również, jak dodać informacje o profilu do tabeli użytkowników.
- ASP.NET MVC i Identity 2.0: Omówienie podstaw autorstwa Johna Attena.
- Potwierdzenie konta i odzyskiwanie hasła przy użyciu tożsamości ASP.NET
- Wprowadzenie do systemu ASP.NET Identity
- Ogłoszenie RTM ASP.NET Identity 2.0.0 przez Pranav Rastogi.
- ASP.NET Identity 2.0: Konfigurowanie weryfikacji konta i autoryzacji Two-Factor przez Johna Attena.