Ćwiczenie — dostosowywanie mechanizmu Identity
W poprzedniej lekcji przedstawiono sposób działania dostosowywania w usłudze ASP.NET Core Identity. W tej lekcji rozszerzysz model danych tożsamości i wprowadzisz odpowiednie zmiany interfejsu użytkownika.
Dostosowywanie interfejsu użytkownika konta użytkownika
W tej sekcji utworzysz i dostosujesz pliki interfejsu użytkownika tożsamości, które będą używane zamiast domyślnej biblioteki klas Razor.
Dodaj do projektu pliki rejestracji użytkownika, które mają zostać zmodyfikowane:
dotnet aspnet-codegenerator identity --dbContext RazorPagesPizzaAuth --files "Account.Manage.EnableAuthenticator;Account.Manage.Index;Account.Register;Account.ConfirmEmail"
W powyższym poleceniu:
- Opcja
--dbContext
informuje narzędzie o istniejącej klasie o nazwieRazorPagesPizzaAuth
pochodzącej od klasyDbContext
. - Opcja
--files
określa rozdzielaną średnikami listę unikatowych plików, które mają zostać dodane do obszaru Tożsamość.Account.Manage.Index
to strona zarządzania profilami. Ta strona zostanie zmodyfikowana w dalszej części tej lekcji.Account.Register
to strona rejestracji użytkownika. Ta strona jest również modyfikowana w tej lekcji.Account.Manage.EnableAuthenticator
iAccount.ConfirmEmail
są rusztowane, ale nie są modyfikowane w tej lekcji.
Napiwek
Uruchom następujące polecenie z katalogu głównego projektu, aby wyświetlić prawidłowe wartości dla
--files
opcji:dotnet aspnet-codegenerator identity --listFiles
Do katalogu Areas/Identity dodawane są następujące pliki:
- Pages/
- _ViewImports.cshtml
- Account/
- _ViewImports.cshtml
- ConfirmEmail.cshtml
- ConfirmEmail.cshtml.cs
- Register.cshtml
- Register.cshtml.cs
- Manage/
- _ManageNav.cshtml
- _ViewImports.cshtml
- EnableAuthenticator.cshtml
- EnableAuthenticator.cshtml.cs
- Index.cshtml
- Index.cshtml.cs
- ManageNavPages.cs
- Opcja
Rozszerzyć IdentityUser
Otrzymujesz nowe wymaganie dotyczące przechowywania nazw użytkowników. Ponieważ klasa domyślna IdentityUser
nie zawiera właściwości dla imienia i nazwiska, należy rozszerzyć klasę RazorPagesPizzaUser
.
Wprowadź następujące zmiany w :Areas/Identity/Data/RazorPagesPizzaUser.cs
Dodaj właściwości
FirstName
iLastName
:using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Identity; namespace RazorPagesPizza.Areas.Identity.Data; public class RazorPagesPizzaUser : IdentityUser { [Required] [MaxLength(100)] public string FirstName { get; set; } = string.Empty; [Required] [MaxLength(100)] public string LastName { get; set; } = string.Empty; }
Właściwości w poprzednim fragmencie kodu reprezentują dodatkowe kolumny, które mają zostać utworzone w tabeli źródłowej
AspNetUsers
. Obie właściwości są wymagane i dlatego są opatrzone atrybutem[Required]
. Ponadto ten atrybut[MaxLength]
wskazuje, że dozwolona jest maksymalna długość 100 znaków. Typ danych kolumny tabeli źródłowej jest zdefiniowany odpowiednio do tego. Wartość domyślna jest przypisywanastring.Empty
, ponieważ kontekst dopuszczalny do wartości null jest włączony w tym projekcie, a właściwości są ciągami bez wartości null.Dodaj następującą instrukcję
using
na początku pliku.using System.ComponentModel.DataAnnotations;
Poprzedni kod rozwiązuje atrybuty adnotacji danych zastosowane do właściwości
FirstName
iLastName
.
Aktualizowanie bazy danych
Po wprowadzeniu zmian w modelu należy wprowadzić zmiany towarzyszące bazie danych.
Upewnij się, że wszystkie zmiany zostały zapisane.
Utwórz i zastosuj migrację platformy EF Core, aby zaktualizować źródłowy magazyn danych:
dotnet ef migrations add UpdateUser dotnet ef database update
Migracja platformy EF Core
UpdateUser
zastosowała skrypt zmiany DDL do schematu tabeliAspNetUsers
. W szczególności zostały dodane kolumnyFirstName
iLastName
, jak pokazano na poniższym fragmencie danych wyjściowych migracji:info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (37ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] ALTER TABLE [AspNetUsers] ADD [FirstName] nvarchar(100) NOT NULL DEFAULT N''; info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (36ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] ALTER TABLE [AspNetUsers] ADD [LastName] nvarchar(100) NOT NULL DEFAULT N'';
Sprawdź bazę danych, aby przeanalizować wpływ
UpdateUser
migracji platformy EF Core naAspNetUsers
schemat tabeli.W okienku SQL Server rozwiń węzeł Kolumny w bazie danych. Tabela AspNetUsers .
Właściwości
FirstName
iLastName
wRazorPagesPizzaUser
klasie odpowiadają kolumnomFirstName
iLastName
na powyższym obrazie. Typ danychnvarchar(100)
został przypisany do każdej z dwóch kolumn z powodu atrybutów[MaxLength(100)]
. Dodano ograniczenie inne niż null, ponieważFirstName
iLastName
w klasie są ciągami bez wartości null. Istniejące wiersze wyświetlają puste ciągi w nowych kolumnach.
Dostosowywanie formularza rejestracji użytkownika
Dodano nowe kolumny dla elementów FirstName
i LastName
. Teraz musisz edytować interfejs użytkownika, aby wyświetlić pasujące pola w formularzu rejestracji.
W pliku Areas/Identity/Pages/Account/Register.cshtml dodaj następujący wyróżniony kod:
<form id="registerForm" asp-route-returnUrl="@Model.ReturnUrl" method="post"> <h2>Create a new account.</h2> <hr /> <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div> <div class="form-floating mb-3"> <input asp-for="Input.FirstName" class="form-control" /> <label asp-for="Input.FirstName"></label> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Input.LastName" class="form-control" /> <label asp-for="Input.LastName"></label> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" /> <label asp-for="Input.Email">Email</label> <span asp-validation-for="Input.Email" class="text-danger"></span> </div>
Powyższy kod dodaje pola tekstowe First name (Imię) i Last name (Nazwisko) do formularza rejestracji użytkownika.
W pliku Areas/Identity/Pages/Account/Register.cshtml.cs dodaj obsługę pól tekstowych imienia i nazwiska.
Dodaj właściwości
FirstName
iLastName
do klasy zagnieżdżonejInputModel
:public class InputModel { [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "First name")] public string FirstName { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "Last name")] public string LastName { get; set; } /// <summary> /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; }
Atrybuty
[Display]
definiują tekst etykiety, która ma być skojarzona z polami tekstowymi.Zmodyfikuj metodę
OnPostAsync
, aby ustawić właściwościFirstName
iLastName
dla obiektuRazorPagesPizza
. Dodaj następujące wyróżnione wiersze:public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl ??= Url.Content("~/"); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { var user = CreateUser(); user.FirstName = Input.FirstName; user.LastName = Input.LastName; await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); var result = await _userManager.CreateAsync(user, Input.Password);
Poprzednia zmiana ustawia właściwości
FirstName
iLastName
na dane wejściowe użytkownika z formularza rejestracji.
Dostosowywanie nagłówka witryny
Zaktualizuj plik Pages/Shared/_LoginPartial.cshtml w celu wyświetlenia imienia i nazwiska zebranych podczas rejestracji użytkownika. Wymagane są wyróżnione wiersze z poniższego fragmentu kodu:
<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
RazorPagesPizzaUser? user = await UserManager.GetUserAsync(User);
var fullName = $"{user?.FirstName} {user?.LastName}";
<li class="nav-item">
<a id="manage" class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello, @fullName!</a>
</li>
UserManager.GetUserAsync(User)
Zwraca obiekt dopuszczany RazorPagesPizzaUser
do wartości null. Operator warunkowy ?.
o wartości null jest używany do uzyskiwania dostępu do FirstName
właściwości i LastName
tylko wtedy, gdy RazorPagesPizzaUser
obiekt nie ma wartości null.
Dostosowywanie formularza zarządzania profilami
Nowe pola zostały dodane do formularza rejestracji użytkownika, ale należy je również dodać do formularza zarządzania profilami, aby istniejący użytkownicy mogli je edytować.
W pliku Areas/Identity/Pages/Account/Manage/Index.cshtml dodaj następujący wyróżniony kod. Zapisz zmiany.
<form id="profile-form" method="post"> <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div> <div class="form-floating mb-3"> <input asp-for="Input.FirstName" class="form-control" /> <label asp-for="Input.FirstName"></label> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Input.LastName" class="form-control" /> <label asp-for="Input.LastName"></label> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-floating mb-3"> <input asp-for="Username" class="form-control" disabled /> <label asp-for="Username" class="form-label"></label> </div>
W pliku Areas/Identity/Pages/Account/Manage/Index.cshtml.cs wprowadź następujące zmiany, aby obsługiwać pola tekstowe imienia i nazwiska.
Dodaj właściwości
FirstName
iLastName
do klasy zagnieżdżonejInputModel
:public class InputModel { [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "First name")] public string FirstName { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 1)] [Display(Name = "Last name")] public string LastName { get; set; } /// <summary> /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> [Phone] [Display(Name = "Phone number")] public string PhoneNumber { get; set; } }
Uwzględnij wyróżnione zmiany w metodzie
LoadAsync
:private async Task LoadAsync(RazorPagesPizzaUser user) { var userName = await _userManager.GetUserNameAsync(user); var phoneNumber = await _userManager.GetPhoneNumberAsync(user); Username = userName; Input = new InputModel { PhoneNumber = phoneNumber, FirstName = user.FirstName, LastName = user.LastName }; }
Poprzedni kod obsługuje pobieranie imion i nazwisk na potrzeby wyświetlania ich w odpowiednich polach tekstowych formularza zarządzania profilami.
Wprowadź wyróżnione zmiany w metodzie
OnPostAsync
. Zapisz zmiany.public async Task<IActionResult> OnPostAsync() { var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } if (!ModelState.IsValid) { await LoadAsync(user); return Page(); } user.FirstName = Input.FirstName; user.LastName = Input.LastName; await _userManager.UpdateAsync(user); var phoneNumber = await _userManager.GetPhoneNumberAsync(user); if (Input.PhoneNumber != phoneNumber) { var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); if (!setPhoneResult.Succeeded) { StatusMessage = "Unexpected error when trying to set phone number."; return RedirectToPage(); } } await _signInManager.RefreshSignInAsync(user); StatusMessage = "Your profile has been updated"; return RedirectToPage(); }
Poprzedni kod obsługuje aktualizowanie imion i nazwisk w tabeli
AspNetUsers
bazy danych.
Konfigurowanie nadawcy wiadomości e-mail z potwierdzeniem
Po pierwszym przetestowaniu aplikacji zarejestrowano użytkownika, a następnie kliknięliśmy link, aby symulować potwierdzenie adresu e-mail użytkownika. Aby wysłać rzeczywistą wiadomość e-mail z potwierdzeniem, należy utworzyć implementację IEmailSender i zarejestrować ją w systemie wstrzykiwania zależności. Aby zachować prostotę, implementacja w tej lekcji nie wysyła wiadomości e-mail na serwer SMTP (Simple Mail Transfer Protocol). Po prostu zapisuje zawartość wiadomości e-mail w konsoli.
Ponieważ zamierzasz wyświetlić wiadomość e-mail w postaci zwykłego tekstu w konsoli programu , należy zmienić wygenerowaną wiadomość, aby wykluczyć tekst zakodowany w formacie HTML. W obszarze Obszary/Tożsamość/Strony/Konto/Register.cshtml.cs znajdź następujący kod:
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
Zmień ją na:
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by visiting the following URL:\r\n\r\n{callbackUrl}");
W okienku Eksplorator kliknij prawym przyciskiem myszy folder RazorPagesPizza\Services i utwórz nowy plik o nazwie EmailSender.cs. Otwórz plik i dodaj następujący kod:
using Microsoft.AspNetCore.Identity.UI.Services; namespace RazorPagesPizza.Services; public class EmailSender : IEmailSender { public EmailSender() {} public Task SendEmailAsync(string email, string subject, string htmlMessage) { Console.WriteLine(); Console.WriteLine("Email Confirmation Message"); Console.WriteLine("--------------------------"); Console.WriteLine($"TO: {email}"); Console.WriteLine($"SUBJECT: {subject}"); Console.WriteLine($"CONTENTS: {htmlMessage}"); Console.WriteLine(); return Task.CompletedTask; } }
Powyższy kod tworzy implementację IEmailSender , która zapisuje zawartość komunikatu w konsoli programu . W rzeczywistej implementacji
SendEmailAsync
połączy się z zewnętrzną usługą poczty lub inną akcją w celu wysłania wiadomości e-mail.W Program.cs dodaj wyróżnione wiersze:
using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using RazorPagesPizza.Areas.Identity.Data; using Microsoft.AspNetCore.Identity.UI.Services; using RazorPagesPizza.Services; var builder = WebApplication.CreateBuilder(args); var connectionString = builder.Configuration.GetConnectionString("RazorPagesPizzaAuthConnection"); builder.Services.AddDbContext<RazorPagesPizzaAuth>(options => options.UseSqlServer(connectionString)); builder.Services.AddDefaultIdentity<RazorPagesPizzaUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<RazorPagesPizzaAuth>(); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddTransient<IEmailSender, EmailSender>(); var app = builder.Build();
Powyższe rejestry
EmailSender
są rejestrowane jakoIEmailSender
w systemie wstrzykiwania zależności.
Testowanie zmian w formularzu rejestracji
To wszystko! Przetestujmy zmiany w formularzu rejestracji i wiadomość e-mail z potwierdzeniem.
Upewnij się, że wszystkie zmiany zostały zapisane.
W okienku terminalu skompiluj projekt i uruchom aplikację za pomocą polecenia
dotnet run
.W przeglądarce przejdź do aplikacji. Wybierz pozycję Wyloguj się, jeśli się zalogowano.
Wybierz pozycję Register (Zarejestruj) i użyj zaktualizowanego formularza, aby zarejestrować nowego użytkownika.
Uwaga
Ograniczenia weryfikacji w polach Imię i Nazwisko odzwierciedlają adnotacje danych we właściwościach
FirstName
iLastName
elementuInputModel
.Po zarejestrowaniu nastąpi przekierowanie do ekranu Potwierdzenia rejestracji. W okienku terminalu przewiń w górę, aby znaleźć dane wyjściowe konsoli podobne do następujących:
Email Confirmation Message -------------------------- TO: jana.heinrich@contoso.com SUBJECT: Confirm your email CONTENTS: Please confirm your account by visiting the following URL: https://localhost:7192/Identity/Account/ConfirmEmail?<query string removed>
Przejdź do adresu URL za pomocą kombinacji Ctrl+. Zostanie wyświetlony ekran potwierdzenia.
Uwaga
Jeśli używasz usługi GitHub Codespaces, może być konieczne dodanie
-7192
do pierwszej części przekazanego adresu URL. Na przykładscaling-potato-5gr4j4-7192.preview.app.github.dev
.Wybierz pozycję Zaloguj się i zaloguj się przy użyciu nowego użytkownika. Nagłówek aplikacji zawiera teraz komunikat Hello, [imię] [nazwisko]! (Witaj, [imię] [nazwisko]!).
W okienku PROGRAMU SQL Server w programie VS Code kliknij prawym przyciskiem myszy bazę danych RazorPagesPizza i wybierz pozycję Nowe zapytanie. Na wyświetlonej karcie wprowadź następujące zapytanie i naciśnij Ctrl+Shift+E, aby go uruchomić.
SELECT UserName, Email, FirstName, LastName FROM dbo.AspNetUsers
Zostanie wyświetlona karta z wynikami podobnymi do następujących:
UserName Email FirstName LastName kai.klein@contoso.com kai.klein@contoso.com jana.heinrich@contoso.com jana.heinrich@contoso.com Jana Heinrich Pierwszy użytkownik zarejestrował się przed dodaniem
FirstName
iLastName
do schematu. Dlatego skojarzonyAspNetUsers
rekord tabeli nie zawiera danych w tych kolumnach.
Testowanie zmian w formularzu zarządzania profilami
Należy również przetestować zmiany wprowadzone w formularzu zarządzania profilami.
W aplikacji internetowej zaloguj się przy użyciu pierwszego utworzonego użytkownika.
Wybierz link Witaj , ! , aby przejść do formularza zarządzania profilami.
Uwaga
Link nie jest wyświetlany poprawnie, ponieważ wiersz tabeli
AspNetUsers
dla tego użytkownika nie zawiera wartościFirstName
iLastName
.Wprowadź prawidłowe wartości First name (Imię) i Last name (Nazwisko). Wybierz pozycję Zapisz.
Nagłówek aplikacji zostanie zaktualizowany do Hello, [Imię] [Nazwisko]! (Witaj, [Imię] [Nazwisko]!).
Aby zatrzymać aplikację, naciśnij Ctrl+C w okienku terminalu w programie VS Code.
Podsumowanie
W tej lekcji dostosowano tożsamość do przechowywania niestandardowych informacji o użytkowniku. Dostosowano również wiadomość e-mail z potwierdzeniem. W następnej lekcji dowiesz się więcej na temat implementowania uwierzytelniania wieloskładnikowego w usłudze Identity.