Cvičení – přizpůsobení architektury Identity
V předchozí lekci jste zjistili, jak přizpůsobení funguje v ASP.NET Core Identity. 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 budou používat 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" --userClass RazorPagesPizzaUser --force
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. - Parametr
--userClass
způsobí vytvoření třídy s názvemRazorPagesPizzaUser
odvozené od třídyIdentityUser
. - Možnost
--force
způsobí přepsání existujících souborů v oblasti Identity .
Tip
Spuštěním následujícího příkazu z kořenového adresáře projektu zobrazte platné hodnoty pro možnost
--files
:dotnet aspnet-codegenerator identity --listFiles
Do adresáře Areas/Identity jsou přidány následující soubory:
- Data/
- RazorPagesPizzaUser.cs
- 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
Soubor Data/RazorPagesPizzaAuth.cs, který existoval před spuštěním předchozího příkazu, byl navíc přepsán, protože byl použit parametr
--force
. Deklarace třídyRazorPagesPizzaAuth
teď odkazuje na nově vytvořený typ uživateleRazorPagesPizzaUser
:public class RazorPagesPizzaAuth : IdentityDbContext<RazorPagesPizzaUser>
Stránky EnableAuthenticator a ConfirmEmail Razor byly vygenerovány, i když se změnily až později v modulu.
- Parametr
V Program.csje potřeba, aby
AddDefaultIdentity
volání vědělo o novém typu uživatele Identita. Začleňte následující zvýrazněné změny. (Příklad je pro čitelnost přeformátovaný.)using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using RazorPagesPizza.Areas.Identity.Data; 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();
Aktualizujte Pages/Shared/_LoginPartial.cshtml , aby byly v horní části zahrnuty následující zvýrazněné změny. Uložte provedené změny.
@using Microsoft.AspNetCore.Identity @using RazorPagesPizza.Areas.Identity.Data @inject SignInManager<RazorPagesPizzaUser> SignInManager @inject UserManager<RazorPagesPizzaUser> UserManager <ul class="navbar-nav">
Předchozí změny aktualizují typ uživatele předaný do
SignInManager<T>
aUserManager<T>
v direktivách@inject
. Místo výchozího typuIdentityUser
se teď odkazuje na uživateleRazorPagesPizzaUser
. Kvůli překladu odkazůRazorPagesPizzaUser
byla přidána direktiva@using
.Soubor Pages/Shared/_LoginPartial.cshtml je fyzicky umístěný mimo oblast Identity. Proto se soubor automaticky neaktualizoval nástrojem pro generování uživatelského rozhraní. Příslušné změny musí být provedeny ručně.
Tip
Jako alternativu k ruční úpravě souboru _LoginPartial.cshtml je možné ho před spuštěním nástroje pro generování kódu odstranit. Soubor _LoginPartial.cshtml se znovu vytvoří s odkazy na novou
RazorPagesPizzaUser
třídu.Aktualizujte soubor Areas/Identity/Data/RazorPagesPizzaUser.cs tak, aby podporoval ukládání a načítání dalších dat profilu uživatele. Proveďte následující změny:
Přidejte vlastnosti
FirstName
aLastName
: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í hodnota je přiřazena, protože kontext s možnoustring.Empty
hodnotou null je v tomto projektu povolen a vlastnosti jsou řetězce, které nelze null.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ž byly provedeny změny modelu, musí být v databázi provedeny doprovodné změny.
Ujistěte se, že jsou všechny 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 vliv
UpdateUser
migrace EF Core naAspNetUsers
schéma tabulky.V podokně SQL Server rozbalte uzel Sloupce na dbo. Tabulka AspNetUsers
Vlastnosti
FirstName
aLastName
veRazorPagesPizzaUser
třídě odpovídají sloupcůmFirstName
aLastName
na předchozím obrázku. Kvůli atributům[MaxLength(100)]
byl k oběma sloupcům přiřazen datový typnvarchar(100)
. Omezení, které nemá hodnotu null, bylo přidáno, protožeFirstName
aLastName
ve třídě jsou řetězce, které nelze použít null. 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ď je potřeba upravit uživatelské rozhraní tak, aby se v registračním formuláři zobrazovala 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"></div> <div class="form-floating"> <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"> <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"> <input asp-for="Input.Email" class="form-control" autocomplete="username" aria-required="true" /> <label asp-for="Input.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>
Přizpůsobení formuláře pro správu profilu
Přidali jste nová pole do formuláře pro registraci uživatelů, ale měli byste je také přidat do formuláře pro správu profilu, aby je stávající uživatelé mohli 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"></div> <div class="form-floating"> <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"> <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"> <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; } [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 potvrzovací e-mailu
Pokud chcete odeslat potvrzovací e-mail, musíte vytvořit implementaci a zaregistrovat ho IEmailSender v systému injektáže závislostí. Aby to nebylo jednoduché, vaše implementace ve skutečnosti neodesílá e-maily na server SMTP. Jenom zapíše obsah e-mailu do konzoly.
Vzhledem k tomu, že se e-mail bude zobrazovat v prostém textu v konzole nástroje , měli byste vygenerovanou zprávu změnit tak, aby se vyloučil text kódovaný ve formátu HTML. V části Areas/Identity/Pages/Account/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 na složku Služby 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 nástroj připojil k externí poštovní službě nebo nějaké jiné akci pro odeslání e-mailu.V souboru 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í se registruje
EmailSender
jako vIEmailSender
systému injektáže závislostí.
Testová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-mailu.
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 pořád přihlášení, vyberte možnost 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 budete přesměrováni na obrazovku potvrzení registrace . V podokně terminálu přejděte nahoru a 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>
Kliknutím se stisknutou klávesou Ctrl+ přejděte na adresuURL. Zobrazí se potvrzovací obrazovka.
Poznámka
Pokud používáte GitHub Codespaces, možná budete muset přidat
-7192
do první části přesměrované adresy URL. Například,scaling-potato-5gr4j4-7192.preview.app.github.dev
.Vyberte Přihlásit se a přihlaste se pod novým uživatelem. Záhlaví aplikace teď obsahuje Hello, [jméno] [příjmení]!.
V podokně SQL Server v editoru VS Code klikněte pravým tlačítkem na databázi RazorPagesPizza a vyberte Nový dotaz. Na kartě, která se zobrazí, zadejte následující dotaz a spusťte ho stisknutím klávesy Ctrl+Shift+E .
SELECT UserName, Email, FirstName, LastName FROM dbo.AspNetUsers
Zobrazí se karta s výsledky podobnými následujícímu:
Uživatelské jméno 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 je zaregistrovaný před přidáním polí
FirstName
aLastName
do schématu.AspNetUsers
Přidružený záznam tabulky tedy 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, které jste udělali ve formuláři pro správu profilu.
Ve webové aplikaci se přihlaste jako první uživatel, který jste vytvořili.
Vyberte odkaz Hello, ! a přejděte na formulář pro správu profilu.
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í. Vyberte Uložit.
Záhlaví aplikace se aktualizuje na Hello, [jméno] [příjmení]!.
Aplikaci zastavíte stisknutím ctrl+C v podokně terminálu v editoru VS Code.
Souhrn
V této lekci jste přizpůsobili identitu tak, aby ukládaly vlastní informace o uživatelích. Také jste přizpůsobili potvrzovací e-mail. V další lekci se dozvíte o implementaci vícefaktorového ověřování ve službě Identity.