Übung: Anpassen der Identität

Abgeschlossen

In der vorherigen Lerneinheit haben Sie gelernt, wie Anpassungen in ASP.NET Core Identity funktionieren. In dieser Einheit erweitern Sie das Identity-Datenmodell und nehmen die entsprechenden Änderungen der Benutzeroberfläche vor.

Anpassen der Benutzerkontodaten

In diesem Abschnitt erstellen Sie die UI-Dateien für Identity, die anstelle der standardmäßigen Razor-Klassenbibliothek verwendet werden sollen, und passen diese entsprechend an.

  1. Fügen Sie die anzupassenden Benutzerregistrierungsdateien zum Projekt hinzu:

    dotnet aspnet-codegenerator identity --dbContext RazorPagesPizzaAuth --files "Account.Manage.EnableAuthenticator;Account.Manage.Index;Account.Register;Account.ConfirmEmail" --userClass RazorPagesPizzaUser --force
    

    Im obigen Befehl:

    • informiert die --dbContext-Option das Tool über die vorhandene abgeleitete DbContext-Klasse namens RazorPagesPizzaAuth.
    • legt die --files-Option eine durch Semikolons getrennte Liste eindeutiger Dateien fest, die zum Bereich Identity hinzugefügt werden sollen.
    • führt die --userClass-Option zur Erstellung einer abgeleiteten IdentityUser-Klasse namens RazorPagesPizzaUser.
    • Die --force-Option bewirkt, dass vorhandene Dateien im Bereich Identity überschrieben werden.

    Tipp

    Führen Sie den folgenden Befehl im Stammverzeichnis des Projekts aus, um gültige Werte für die --files-Option anzuzeigen: dotnet aspnet-codegenerator identity --listFiles

    Die folgenden Dateien werden zum Verzeichnis Areas/Identity hinzugefügt:

    • 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

    Darüber hinaus wurde die Data/RazorPagesPizzaAuth.cs-Datei, die vor der Ausführung des obigen Befehls vorhanden war, überschrieben, da die --force-Option verwendet wurde. Die Deklaration der RazorPagesPizzaAuth-Klasse verweist nun auf den neu erstellten Benutzertyp RazorPagesPizzaUser:

    public class RazorPagesPizzaAuth : IdentityDbContext<RazorPagesPizzaUser>
    

    Für die Razor-Seiten EnableAuthenticator und ConfirmEmail wurde ein Gerüst erstellt. Sie werden aber erst später in diesem Modul angepasst.

  2. Im Aufruf von AddDefaultIdentity in Program.cs muss der neue Identity-Benutzertyp angegeben werden. Implementieren Sie die folgenden hervorgehobenen Änderungen. (Beispiel für Lesbarkeit umformatiert.)

    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();
    
  3. Aktualisieren Sie Pages/Shared/_LoginPartial.cshtml, um die folgenden hervorgehobenen Änderungen oben einzufügen. Speichern Sie die Änderungen.

    @using Microsoft.AspNetCore.Identity
    @using RazorPagesPizza.Areas.Identity.Data
    @inject SignInManager<RazorPagesPizzaUser> SignInManager
    @inject UserManager<RazorPagesPizzaUser> UserManager
    
    <ul class="navbar-nav">
    

    Mit den obigen Änderungen wird der Benutzertyp geändert, der in den @inject-Anweisungen an SignInManager<T> und UserManager<T> übergeben wird. Anstelle des IdentityUser-Standardtyps, wird nun auf den RazorPagesPizzaUser-Benutzer verwiesen. Die @using-Anweisung wurde hinzugefügt, um die RazorPagesPizzaUser-Verweise aufzulösen.

    Pages/Shared/_LoginPartial.cshtml befindet sich physisch außerhalb des Bereichs Identity. Die Datei wurde daher nicht automatisch vom Gerüstgeneratortool aktualisiert. Die entsprechenden Änderungen müssen manuell vorgenommen werden.

    Tipp

    Alternativ zum manuellen Bearbeiten der _LoginPartial.cshtml-Datei kann sie auch vor Ausführung des Erstellungstools gelöscht werden. Die _LoginPartial.cshtml-Datei wird mit Verweisen auf die neue RazorPagesPizzaUser-Klasse neu erstellt.

  4. Ändern Sie Areas/Identity/Data/RazorPagesPizzaUser.cs, um das Speichern und Abrufen zusätzlicher Benutzerprofildaten zu unterstützen. Nehmen Sie die folgenden Änderungen vor:

    1. Fügen Sie die Eigenschaften FirstName und LastName hinzu:

      public class RazorPagesPizzaUser : IdentityUser
      {
          [Required]
          [MaxLength(100)]
          public string FirstName { get; set; } = string.Empty;
      
          [Required]
          [MaxLength(100)]
          public string LastName { get; set; } = string.Empty;
      }
      

      Die Eigenschaften im vorherigen Codeausschnitt stellen weitere Spalten dar, die in der zugrunde liegenden AspNetUsers-Tabelle erstellt werden. Beide Eigenschaften sind erforderlich und werden daher mit dem [Required]-Attribut versehen. Darüber hinaus gibt das [MaxLength]-Attribut eine zulässige maximale Länge von 100 Zeichen an. Der Datentyp der zugrunde liegenden Tabellenspalte wird entsprechend definiert. Der Standardwert string.Empty wird zugewiesen, weil der Nullwerte zulassende Kontext in diesem Projekt aktiviert ist und die Eigenschaften nicht nullbare Zeichenfolgen sind.

    2. Fügen Sie am Anfang der Datei die folgende using-Anweisung hinzu.

      using System.ComponentModel.DataAnnotations;
      

      Der obige Code löst die Datenanmerkungsattribute auf, die auf die Eigenschaften FirstName und LastName angewendet werden.

Aktualisieren der Datenbank

Nachdem die Modelländerungen vorgenommen wurden, müssen entsprechende Änderungen an der Datenbank vorgenommen werden.

  1. Vergewissern Sie sich, dass alle Ihre Änderungen gespeichert werden.

  2. Erstellen Sie eine EF Core-Migration, und wenden Sie sie an, um den zugrunde liegenden Datenspeicher zu aktualisieren:

    dotnet ef migrations add UpdateUser
    dotnet ef database update
    

    Die EF Core-Migration UpdateUser wendet ein DDL-Änderungsskript auf das Schema der AspNetUsers-Tabelle an. Wie in der folgenden Migrationsausgabe zu sehen ist, wurden die Spalten FirstName und LastName hinzugefügt:

    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'';
    
  3. Untersuchen Sie die Datenbank, um die Auswirkungen der EF Core-Migration UpdateUser auf das Schema der AspNetUsers-Tabelle zu analysieren.

    Erweitern Sie im Bereich SQL Server den Knoten Spalten für die dbo.AspNetUsers-Tabelle.

    Screenshot: Schema der AspNetUsers-Tabelle

    Die Eigenschaften FirstName und LastName in der Klasse RazorPagesPizzaUser entsprechen den Spalten FirstName und LastName in der obigen Abbildung. Beiden Spalten wurde aufgrund der [MaxLength(100)]-Attribute der Datentyp nvarchar(100) zugewiesen. Die Nicht-NULL-Einschränkung wurde hinzugefügt, da FirstName und LastName in der Klasse nicht nullbare Zeichenfolgen sind. Vorhandene Zeilen zeigen in den neuen Spalten leere Zeichenfolgen an.

Anpassen des Benutzerregistrierungsformulars

Sie haben neue Spalten für FirstName und LastName hinzugefügt. Jetzt müssen Sie die Benutzeroberfläche bearbeiten, um die entsprechenden Felder im Registrierungsformular anzuzeigen.

  1. Fügen Sie das folgende hervorgehobene Markup in Areas/Identity/Pages/Account/Register.cshtml ein:

    <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>
    

    Mit dem obigen Markup werden die Textfelder First name und Last name (Vorname und Nachname) zum Benutzerregistrierungsformular hinzugefügt.

  2. Fügen Sie in Areas/Identity/Pages/Account/Register.cshtml.cs Unterstützung für die Namenstextfelder ein.

    1. Fügen Sie die Eigenschaften FirstName und LastName zur geschachtelten Klasse InputModel hinzu:

      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; }
      

      Die [Display]-Attribute definieren den Bezeichnungstext, der den Textfeldern zugeordnet werden soll.

    2. Ändern Sie die OnPostAsync-Methode, um die Eigenschaften FirstName und LastName für das RazorPagesPizza-Objekt festzulegen. Fügen Sie die folgenden hervorgehobenen Zeilen hinzu:

      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);
      
      

      Durch diese Änderung wird für die Eigenschaften FirstName und LastName die Benutzereingabe aus dem Registrierungsformular festgelegt.

Anpassen des Websiteheaders

Ändern Sie Pages/Shared/_LoginPartial.cshtml, um die während der Benutzerregistrierung erfassten Vor- und Nachnamen anzuzeigen. Sie benötigen die hervorgehobenen Zeilen aus dem folgenden Codeausschnitt:

<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>

Anpassen des Profilverwaltungsformulars

Sie haben die neuen Felder zum Benutzerregistrierungsformular hinzugefügt. Sie sollten sie aber auch dem Profilverwaltungsformular hinzufügen, damit vorhandene Benutzer sie bearbeiten können.

  1. Fügen Sie das folgende hervorgehobene Markup in Areas/Identity/Pages/Account/Manage/Index.cshtml ein: Speichern Sie die Änderungen.

    <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>
    
  2. Nehmen Sie die folgenden Änderungen in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs vor, um die Namenstextfelder zu unterstützen.

    1. Fügen Sie die Eigenschaften FirstName und LastName zur geschachtelten Klasse InputModel hinzu:

      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; }
      }
      
    2. Fügen Sie die folgenden hervorgehobenen Änderungen in die LoadAsync-Methode ein:

      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
          };
      }
      

      Der obige Code unterstützt das Abrufen des Vor- und Nachnamen, um diese in den entsprechenden Feldern des Profilverwaltungsformulars anzuzeigen.

    3. Fügen Sie die hervorgehobenen Änderungen in die OnPostAsync-Methode ein. Speichern Sie die Änderungen.

      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();
      }
      

      Der obige Code unterstützt das Aktualisieren des Vor- und Nachnamen im der AspNetUsers-Tabelle der Datenbank.

Konfigurieren des Bestätigungs-E-Mail-Absenders

Um die Bestätigungs-E-Mail zu senden, müssen Sie eine Implementierung von IEmailSender erstellen und im Abhängigkeitsinjektionssystem registrieren. Um die Implementierung einfach zu halten, werden tatsächlich keine E-Mails an einen SMTP-Server gesendet. Der E-Mail-Inhalt wird einfach an der Konsole ausgegeben.

  1. Da Sie die E-Mail in der Konsole als unformatierten Text anzeigen möchten, sollten Sie die generierte Nachricht ändern, um HTML-codierten Text auszuschließen. Suchen Sie in Areas/Identity/Pages/Account/Register.cshtml.cs den folgenden Code:

    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
    

    Ändern Sie ihn in:

    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
        $"Please confirm your account by visiting the following URL:\r\n\r\n{callbackUrl}");
    
  2. Klicken Sie im Explorer-Bereich mit der rechten Maustaste auf den Ordner Dienste, und erstellen Sie eine neue Datei namens EmailSender.cs. Öffnen Sie die Datei, und fügen Sie den folgenden Code hinzu:

    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;
        }
    }
    

    Der obige Code erstellt eine Implementierung von IEmailSender, die den Inhalt der Nachricht in der Konsole ausgibt. In einer realen Implementierung würde SendEmailAsync eine Verbindung mit einem externen E-Mail-Dienst herstellen oder eine anderen Aktion zum Senden der E-Mails ausführen.

  3. Fügen Sie in Program.cs die hervorgehobenen Zeilen hinzu:

    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();
    

    Dadurch wird EmailSender im Abhängigkeitsinjektionssystem als IEmailSender registriert.

Testen der Änderungen am Registrierungsformular

Das ist alles! Lassen Sie uns die Änderungen des Registrierungsformulars und der Bestätigungs-E-Mail testen.

  1. Vergewissern Sie sich, dass Sie alle Änderungen gespeichert haben.

  2. Erstellen Sie im Terminalbereich das Projekt, und führen Sie die App mit dotnet run aus.

  3. Navigieren Sie im Browser zur App. Klicken Sie auf Logout (Abmelden), wenn Sie noch angemeldet sind.

  4. Klicken Sie auf Register (Registrieren), und verwenden Sie das neue Formular, um einen neuen Benutzer zu registrieren.

    Hinweis

    Die Validierungseinschränkungen für die Felder Vorname und Nachname stellen die Datenanmerkungen der Eigenschaften FirstName und LastName von InputModel dar.

  5. Nach der Registrierung werden Sie zum Bildschirm zum Bestätigen der Registrierung umgeleitet. Scrollen Sie im Terminalbereich nach oben, um die folgende Konsolenausgabe zu finden:

    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>
    

    Navigieren Sie mit STRG+Klick zur URL. Der Bestätigungsbildschirm wird angezeigt.

    Hinweis

    Wenn Sie GitHub Codespaces verwenden, müssen Sie möglicherweise -7192 zum ersten Teil der weitergeleiteten URL hinzufügen. Beispiel: scaling-potato-5gr4j4-7192.preview.app.github.dev.

  6. Wählen Sie Anmelden aus, und melden Sie sich mit dem neuen Benutzer an. Der Header der App enthält nun Hello, [Vorname] [Nachname]!.

  7. Klicken Sie im Bereich SQL Server in VS Code mit der rechten Maustaste auf die Datenbank RazorPagesPizza, und wählen Sie Neue Abfrage aus. Geben Sie in der angezeigten Registerkarte die folgende Abfrage ein, und drücken Sie STRG+UMSCHALTASTE+E,um sie auszuführen.

    SELECT UserName, Email, FirstName, LastName
    FROM dbo.AspNetUsers
    

    Eine Registerkarte mit ähnlichen Ergebnissen wie folgt wird angezeigt:

    UserName Email FirstName LastName
    kai.klein@contoso.com kai.klein@contoso.com
    jana.heinrich@contoso.com jana.heinrich@contoso.com Jana Heinrich

    Der erste Benutzer wurde registriert, bevor FirstName und LastName zum Schema hinzugefügt wurden. Daher enthält der zugehörige Tabelleneintrag AspNetUsers in diesen Spalten keine Daten.

Testen der Änderungen am Profilverwaltungsformular

Sie sollten auch die Änderungen testen, die Sie am Profilverwaltungsformular vorgenommen haben.

  1. Melden Sie sich mit dem zuerst erstellten Benutzerkonto bei der Web-App an.

  2. Klicken Sie auf den Link Hallo,, um das Profilverwaltungsformular aufzurufen.

    Hinweis

    Der Link wird nicht ordnungsgemäß angezeigt, weil die AspNetUsers-Zeile der Tabelle für diesen Benutzer keine Werte für FirstName und LastName enthält.

  3. Geben Sie gültige Werte für Vorname und Nachname ein. Klicken Sie auf Speichern.

    Der Header der App wird in Hello, [Vorname] [Nachname]! geändert.

  4. Drücken Sie im Terminalbereich in VS Code STRG+C, um die App zu beenden.

Zusammenfassung

In dieser Einheit haben Sie Identity angepasst, um benutzerdefinierte Benutzerinformationen zu speichern. Sie haben außerdem die Bestätigungs-E-Mail angepasst. In der nächsten Lerneinheit erfahren Sie mehr über die Implementierung der Multi-Faktor-Authentifizierung in Identity.