Cvičení – přizpůsobení architektury Identity
V předchozí lekci jste zjistili, jak funguje přizpůsobení v ASP.NET Základní identitě. V této lekci rozšíříte datový model identity a provedete odpovídající změny uživatelského rozhraní.
Přizpůsobení dat uživatelského účtu
V této části vytvoříte a přizpůsobíte soubory uživatelského rozhraní identity, které se použijí místo výchozí knihovny tříd Razor.
Přidejte do projektu registrační soubory uživatele, které mají být upraveny:
dotnet aspnet-codegenerator identity --dbContext RazorPagesPizzaAuth --files "Account.Manage.EnableAuthenticator;Account.Manage.Index;Account.Register;Account.ConfirmEmail"
V předcházejícím příkazu:
- Parametr
--dbContext
informuje nástroj o existující tříděRazorPagesPizzaAuth
odvozené od třídyDbContext
. - Parametr
--files
určuje středníkem oddělený seznam jedinečných souborů, které se mají přidat do oblasti Identity.Account.Manage.Index
je stránka pro správu profilu. Tato stránka se později v této lekci upraví.Account.Register
je stránka registrace uživatele. Tato stránka je také upravena v této lekci.Account.Manage.EnableAuthenticator
aAccount.ConfirmEmail
jsou vygenerovány, ale nejsou upraveny v této lekci.
Tip
Spuštěním následujícího příkazu z kořenového adresáře projektu zobrazte platné hodnoty pro příslušnou
--files
možnost:dotnet aspnet-codegenerator identity --listFiles
Do adresáře Areas/Identity jsou přidány následující soubory:
- 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
- Parametr
Rozšířit IdentityUser
Máte nový požadavek na uložení jmen uživatelů. Vzhledem k tomu, že výchozí IdentityUser
třída neobsahuje vlastnosti pro křestní jména a příjmení, musíte třídu rozšířit RazorPagesPizzaUser
.
Proveďte následující změny Areas/Identity/Data/RazorPagesPizzaUser.cs:
Přidejte vlastnosti
FirstName
aLastName
: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; }
Vlastnosti v předchozím fragmentu kódu představují další sloupce, které se mají vytvořit v podkladové tabulce
AspNetUsers
. Obě vlastnosti jsou povinné a jsou proto opatřené atributem[Required]
. Atribut[MaxLength]
navíc udává, že maximální povolená délka je 100 znaků. Datový typ sloupce podkladové tabulky je definován odpovídajícím způsobem. Výchozí hodnotastring.Empty
je přiřazena, protože v tomto projektu je povolen kontext s možnou hodnotou null a vlastnosti jsou nenulové řetězce.Na začátek souboru přidejte následující příkaz
using
.using System.ComponentModel.DataAnnotations;
Předchozí kód přeloží atributy datových poznámek použitých na vlastnosti
FirstName
aLastName
.
Aktualizace databáze
Teď, když jsou změny modelu provedeny, musí být v databázi provedeny doprovodné změny.
Ujistěte se, že jsou všechny vaše změny uložené.
Vytvořením a použitím migrace EF Core aktualizujte podkladové úložiště dat:
dotnet ef migrations add UpdateUser dotnet ef database update
Při migraci EF Core
UpdateUser
se na schéma tabulkyAspNetUsers
použil změnový skript DDL. Konkrétně byly přidány sloupceFirstName
aLastName
, jak je vidět na následujícím výňatku z výstupu migrace: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'';
Prozkoumejte databázi a analyzujte účinek
UpdateUser
migrace EF Core naAspNetUsers
schéma tabulky.V podokně SQL Server rozbalte uzel Sloupce v dbo. Tabulka AspNetUsers
LastName
VlastnostiFirstName
a vlastnosti veRazorPagesPizzaUser
třídě odpovídajíFirstName
sloupcůmLastName
v předchozím obrázku. Kvůli atributům[MaxLength(100)]
byl k oběma sloupcům přiřazen datový typnvarchar(100)
. Bylo přidáno omezení, které není null, protožeFirstName
veLastName
třídě jsou nenulové řetězce. U existujících řádků jsou v nových sloupcích prázdné řetězce.
Přizpůsobení formuláře pro registraci uživatele
Přidali jste nové sloupce pro FirstName
a LastName
. Teď potřebujete upravit uživatelské rozhraní, aby se v registračním formuláři zobrazila odpovídající pole.
V souboru Areas/Identity/Pages/Account/Register.cshtml přidejte následující zvýrazněný kód:
<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>
V předchozím kódu jsou do formuláře pro registraci uživatele přidána textová pole pro jméno a příjmení.
V souboru Areas/Identity/Pages/Account/Register.cshtml.cs přidejte podporu pro textová pole se jménem.
Do vnořené třídy
InputModel
přidejte vlastnostiFirstName
aLastName
: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; }
Atributy
[Display]
definují text popisku, který má být přidružen k těmto textovým polím.Úpravou metody
OnPostAsync
nastavte vlastnostiFirstName
aLastName
u objektuRazorPagesPizza
. Přidejte následující zvýrazněné řádky: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);
Předchozí změna nastaví vlastnosti
FirstName
aLastName
na zadání uživatele z registračního formuláře.
Přizpůsobení záhlaví webu
Aktualizací souboru Pages/Shared/_LoginPartial.cshtml zobrazte jméno a příjmení shromážděné během registrace uživatele. Jsou zapotřebí zvýrazněné řádky v následujícím fragmentu kódu:
<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)
vrátí objekt s možnou RazorPagesPizzaUser
hodnotou null. Podmíněný ?.
operátor null se používá pro přístup k vlastnostem FirstName
a LastName
přístup pouze v případě RazorPagesPizzaUser
, že objekt není null.
Přizpůsobení formuláře pro správu profilu
Do registračního formuláře uživatele jste přidali nová pole, ale měli byste je také přidat do formuláře pro správu profilů, aby je mohli stávající uživatelé upravovat.
V souboru Areas/Identity/Pages/Account/Manage/Index.cshtml přidejte následující zvýrazněný kód. Uložte provedené změny.
<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>
V souboru Areas/Identity/Pages/Account/Manage/Index.cshtml.cs proveďte následující změny pro podporu textových polí se jménem.
Do vnořené třídy
InputModel
přidejte vlastnostiFirstName
aLastName
: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; } }
Do metody
LoadAsync
začleňte zvýrazněné změny: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 }; }
Předchozí kód umožňuje načtení jména a příjmení, které se zobrazí v odpovídajících textových polích formuláře pro správu profilu.
Do metody
OnPostAsync
začleňte zvýrazněné změny. Uložte provedené změny.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(); }
Předchozí kód umožňuje aktualizaci jména a příjmení v tabulce databáze
AspNetUsers
.
Konfigurace odesílatele e-mailu s potvrzením
Při prvním otestování aplikace jste zaregistrovali uživatele a potom jste klikli na odkaz pro simulaci potvrzení e-mailové adresy uživatele. Pokud chcete odeslat skutečný potvrzovací e-mail, musíte vytvořit implementaci IEmailSender a zaregistrovat ji v systému injektáže závislostí. Aby bylo všechno jednoduché, implementace v této lekci ve skutečnosti neodesílá e-mail na server SMTP (Simple Mail Transfer Protocol). Jenom zapíše e-mailový obsah do konzoly.
Vzhledem k tomu, že e-mail zobrazíte ve formátu prostého textu v konzole, měli byste vygenerovanou zprávu změnit tak, aby se vyloučil text kódovaný ve formátu HTML. V oblastech, identitě, stránkách, účtu nebo Register.cshtml.cs vyhledejte následující kód:
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
Změňte ho na:
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by visiting the following URL:\r\n\r\n{callbackUrl}");
V podokně Průzkumník klikněte pravým tlačítkem myši na složku RazorPagesPizza\Services a vytvořte nový soubor s názvem EmailSender.cs. Otevřete soubor a přidejte následující kód:
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; } }
Předchozí kód vytvoří implementaci IEmailSender , která zapíše obsah zprávy do konzoly. V reálné implementaci
SendEmailAsync
by se připojil k externí poštovní službě nebo k nějaké jiné akci pro odesílání e-mailů.V Program.cs přidejte zvýrazněné řádky:
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();
Předchozí registrace jsou
IEmailSender
v systému injektáže závislostíEmailSender
.
Otestování změn registračního formuláře
To je všechno! Pojďme otestovat změny registračního formuláře a potvrzovací e-mail.
Ujistěte se, že jste uložili všechny změny.
V podokně terminálu sestavte projekt a spusťte aplikaci pomocí
dotnet run
příkazu .V prohlížeči přejděte na aplikaci. Pokud jste přihlášení, vyberte Odhlášení .
Vyberte možnost registrace a pomocí aktualizovaného formuláře zaregistrujte nového uživatele.
Poznámka:
V ověřovacích omezeních u polí pro jméno a příjmení jsou promítnuty datové poznámky vlastností
FirstName
aLastName
proInputModel
.Po registraci se přesměruje na potvrzovací obrazovku registrace . V podokně terminálu vyhledejte výstup konzoly podobný následujícímu:
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>
Přejděte na adresu URL stisknutím klávesy Ctrl+. Zobrazí se potvrzovací obrazovka.
Poznámka:
Pokud používáte GitHub Codespaces, možná budete muset přidat
-7192
první část přeposílané adresy URL. Napříkladscaling-potato-5gr4j4-7192.preview.app.github.dev
.Vyberte Přihlášení a přihlaste se pomocí nového uživatele. Záhlaví aplikace teď obsahuje Hello, [jméno] [příjmení]!.
V podokně SQL Server v editoru VS Code klikněte pravým tlačítkem myši na databázi RazorPagesPizza a vyberte Nový dotaz. Na kartě, která se zobrazí, zadejte následující dotaz a stisknutím kláves Ctrl+Shift+E ho spusťte.
SELECT UserName, Email, FirstName, LastName FROM dbo.AspNetUsers
Zobrazí se karta s výsledky podobnými následujícímu:
UserName E-mail FirstName LastName kai.klein@contoso.com kai.klein@contoso.com jana.heinrich@contoso.com jana.heinrich@contoso.com Jana Heinrich První uživatel se zaregistroval před přidáním
FirstName
aLastName
do schématu.AspNetUsers
Přidružený záznam tabulky proto v těchto sloupcích neobsahuje data.
Otestování změn ve formuláři pro správu profilu
Měli byste také otestovat změny provedené ve formuláři pro správu profilů.
Ve webové aplikaci se přihlaste pomocí prvního uživatele, který jste vytvořili.
Vyberte odkaz Hello, ! a přejděte do formuláře pro správu profilů.
Poznámka:
Odkaz se nezobrazuje správně, protože řádek tabulky
AspNetUsers
pro tohoto uživatele neobsahuje hodnoty políFirstName
aLastName
.Zadejte platné hodnoty pro jméno a příjmení. Zvolte Uložit.
Záhlaví aplikace se aktualizuje na Hello, [jméno] [příjmení]!.
Pokud chcete aplikaci zastavit, stiskněte ctrl+C v podokně terminálu v editoru VS Code.
Shrnutí
V této lekci jste přizpůsobili identitu tak, aby ukládaly vlastní informace o uživatelích. Přizpůsobili jste si také potvrzovací e-mail. V další lekci se dozvíte o implementaci vícefaktorového ověřování v identity.