Hinzufügen, Herunterladen und Löschen von benutzerdefinierten Benutzerdaten zu Identity in einem ASP.NET Core Projekt

In diesem Artikel wird gezeigt, wie:

  • Fügen Sie einer ASP.NET Core Web-App benutzerdefinierte Benutzerdaten hinzu.
  • Markieren Sie das benutzerdefinierte Benutzerdatenmodell mit dem PersonalDataAttribute Attribut, damit es automatisch zum Herunterladen und Löschen verfügbar ist. Die Daten, die heruntergeladen und gelöscht werden können, tragen dazu bei, die DSGVO-Anforderungen zu erfüllen.

Das Projektbeispiel wird aus einer Razor Pages-Web-App erstellt, die Anweisungen für eine ASP.NET Core MVC-Web-App sind jedoch ähnlich.

Anzeigen oder Herunterladen von Beispielcode (Anleitung zum Herunterladen)

Prerequisites

.NET 10.0 SDK

Schnellstart: Hinzufügen von zwei benutzerdefinierten Eigenschaften

Für den gängigen Fall beim Hinzufügen von zwei benutzerdefinierten Eigenschaften wie FirstName z. B. und LastName:

  1. Erstellen eines neuen ASP.NET Core Projekts mit der Authentifizierung einzelner Konten
  2. Gerüst Identity zum Überschreiben der Seiten „Registrieren“ und „Verwalten/Index“
  3. Fügen Sie benutzerdefinierte Eigenschaften wie FirstName und LastName zur Benutzerklasse mit dem [PersonalData] Attribut hinzu.
  4. Aktualisieren Sie die Seiten "Register" und "Manage/Index", um Eingabefelder für Ihre Eigenschaften einzuschließen.
  5. Erstellen und Anwenden einer Migration
  6. Testen Sie die Registrierung und Profilverwaltung.

In den folgenden Abschnitten finden Sie detaillierte schrittweise Anleitungen für diesen Prozess.

Erstellen einer Web-App

Sie können entweder eine Razor Seiten- oder MVC-Web-App erstellen. Beide verwenden die gleiche IdentityRazor Benutzeroberfläche für Seiten.

Razor Seiten

  • Wählen Sie im Visual Studio-Menü DateiNeu>Projekt/Lösung... aus.
  • Auswählen ASP.NET Core Web App (Razor Seiten)>Weiter
  • Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll. Wählen Sie Weiteraus.
  • Auswählen des Authentifizierungstyps>"Einzelne Konten>erstellen"
  • Erstellen Sie das Projekt, und führen Sie es aus.

MVC

  • Wählen Sie im Visual Studio-Menü DateiNeu>Projekt/Lösung... aus.
  • Wählen Sie ASP.NET Core Web App (Modell-View-Controller)>Weiter
  • Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll. Wählen Sie Weiteraus.
  • Auswählen des Authentifizierungstyps>"Einzelne Konten>erstellen"
  • Erstellen Sie das Projekt, und führen Sie es aus.

Note

Identity Die Benutzeroberfläche wird als Razor Klassenbibliothek implementiert. Wenn Sie ein MVC-Projekt mit Authentifizierung erstellen, werden die Identity-Seiten aus der Razor-Klassenbibliothek bereitgestellt. Das bedeutet, dass Ihr MVC-Projekt Razor-Seiten für Identity verwendet, obwohl im restlichen Projekt MVC-Controller und -Ansichten verwendet werden.

Führen Sie den Identity-Gerüstbau aus.

  • Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >Add>Neues Gerüstelement.
  • Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
  • Wählen Sie im Dialogfeld Identity hinzufügen die folgenden Optionen aus:
    • Auswählen der vorhandenen Layoutdatei ~/Pages/Shared/_Layout.cshtml für Razor Seiten oder ~/Views/Shared/_Layout.cshtml für MVC
    • Wählen Sie die folgenden Dateien zum Überschreiben aus:
      • Account/Register
      • Account/Manage/Index
    • Wählen Sie die + Schaltfläche aus, um eine neue Datenkontextklasse zu erstellen. Übernehmen Sie den Typ (WebApp1.Models.WebApp1Context , wenn das Projekt "WebApp1" heißt).
    • Wählen Sie die + Schaltfläche aus, um eine neue Benutzerklasse zu erstellen. Akzeptieren Sie den Typ (WebApp1User, wenn das Projekt WebApp1 heißt) und klicken Sie auf >.
  • Wählen Sie Hinzufügen aus.

Befolgen Sie die Anweisungen in Migrationen, UseAuthentication und Layout, um die folgenden Schritte auszuführen:

  • Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
  • Fügen Sie UseAuthentication zu Program.cs hinzu
  • Fügen Sie <partial name="_LoginPartial" /> in die Layoutdatei ein.
  • Testen Sie die App:
    • Registrieren eines Benutzers
    • Wählen Sie den neuen Benutzernamen aus (neben dem Link " Abmelden "). Möglicherweise müssen Sie das Fenster erweitern oder das Navigationsleistensymbol auswählen, um den Benutzernamen und andere Links anzuzeigen.
    • Wählen Sie die Registerkarte " Persönliche Daten " aus.
    • Wählen Sie die Schaltfläche "Herunterladen " aus, und überprüfen Sie die PersonalData.json Datei.
    • Testen Sie die Schaltfläche "Löschen ", die den angemeldeten Benutzer löscht.

Hinzufügen von benutzerdefinierten Benutzerdaten zur Identity DB

Aktualisieren Sie die IdentityUser abgeleitete Klasse mit benutzerdefinierten Eigenschaften. Wenn Sie das Projekt WebApp1 benannt haben, wird die Datei benannt Areas/Identity/Data/WebApp1User.cs. Aktualisieren Sie die Datei mit dem folgenden Code:

using Microsoft.AspNetCore.Identity;

namespace WebApp1.Areas.Identity.Data;

public class WebApp1User : IdentityUser
{
    [PersonalData]
    public string? Name { get; set; }
    [PersonalData]
    public DateTime DOB { get; set; }
}

Eigenschaften mit dem PersonalData-Attribut sind:

  • Gelöscht, wenn die Seite Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete aufruft.
  • Sie werden durch die Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor-Seite in die heruntergeladenen Daten einbezogen.

Aktualisieren der Account/Manage/Index.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs mit dem folgenden hervorgehobenen Code:

public class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    /// <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>
    public string Username { get; set; }

    // Remaining API warnings omited.

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    private async Task LoadAsync(WebApp1User user)
    {
        var userName = await _userManager.GetUserNameAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            PhoneNumber = phoneNumber
        };
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        await LoadAsync(user);
        return Page();
    }

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

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

        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        await _userManager.UpdateAsync(user);
        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Manage/Index.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-floating">
                <input asp-for="Username" class="form-control" disabled />
                <label asp-for="Username" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.Name" class="form-control" />
                <label asp-for="Input.Name" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <label asp-for="Input.PhoneNumber" class="form-label"></label>
                <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Aktualisieren der Account/Register.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs mit dem folgenden hervorgehobenen Code:

    public class RegisterModel : PageModel
    {
        private readonly SignInManager<WebApp1User> _signInManager;
        private readonly UserManager<WebApp1User> _userManager;
        private readonly IUserStore<WebApp1User> _userStore;
        private readonly IUserEmailStore<WebApp1User> _emailStore;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;

        public RegisterModel(
            UserManager<WebApp1User> userManager,
            IUserStore<WebApp1User> userStore,
            SignInManager<WebApp1User> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _userStore = userStore;
            _emailStore = GetEmailStore();
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        /// <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>
        [BindProperty]
        public InputModel Input { get; set; }

        // Remaining API warnings omited.
        public string ReturnUrl { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public class InputModel
        {
            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Full name")]
            public string Name { get; set; }

            [Required]
            [Display(Name = "Birth Date")]
            [DataType(DataType.Date)]
            public DateTime DOB { get; set; }

            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }


        public async Task OnGetAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = CreateUser();

                user.Name = Input.Name;
                user.DOB = Input.DOB;

                await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
                await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
                var result = await _userManager.CreateAsync(user, Input.Password);

                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var userId = await _userManager.GetUserIdAsync(user);
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);

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

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

        private WebApp1User CreateUser()
        {
            try
            {
                return Activator.CreateInstance<WebApp1User>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
                    $"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }

        private IUserEmailStore<WebApp1User> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore<WebApp1User>)_userStore;
        }
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Register.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <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.Name" class="form-control" />
                <label asp-for="Input.Name"></label>
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB"></label>
                <span asp-validation-for="Input.DOB" 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>
            <div class="form-floating">
                <input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.Password"></label>
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.ConfirmPassword"></label>
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h3>Use another service to register.</h3>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
                            about setting up this ASP.NET application to support logging in via external services</a>.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins!)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Projekt erstellen.

Aktualisieren des Layouts

Anweisungen zum Hinzufügen von Anmelde- und Abmeldelinks zu jeder Seite finden Sie unter Layoutänderungen .

Hinzufügen einer Migration für die benutzerdefinierten Benutzerdaten

In der Visual Studio Paket-Manager Console:

Add-Migration CustomUserData
Update-Database

Benutzerdefinierte Daten erstellen, anzeigen, herunterladen und löschen testen

Testen der App:

  • Registrieren sie einen neuen Benutzer.
  • Zeigen Sie die benutzerdefinierten Benutzerdaten auf der /Identity/Account/Manage Seite an.
  • Laden Sie die persönlichen Daten der Benutzer von der /Identity/Account/Manage/PersonalData Seite herunter, und zeigen Sie sie an.

Troubleshooting

Wenn Probleme auftreten:

Symptom Wahrscheinliche Ursache Lösung
Benutzerdefinierte Felder werden nicht im Registrierungsformular angezeigt Vergessen, Register.cshtml zu aktualisieren Stellen Sie sicher, dass Sie ein Gerüst Areas/Identity/Pages/Account/Register.cshtml erstellt und geändert haben
Benutzerdefinierte Daten werden nicht gespeichert. Vergessen, eine Migrations- oder Aktualisierungsdatenbank hinzuzufügen Führen Sie dotnet ef migrations add CustomUserData aus und dann dotnet ef database update
Benutzerdefinierte Felder, die nicht in heruntergeladenen Daten enthalten sind Fehlendes [PersonalData] Attribut Hinzufügen eines [PersonalData] Attributs zu Eigenschaften in Ihrer Benutzerklasse
Build-Fehler nach der Gerüsterstellung Paketversionskonflikt Stellen Sie sicher, dass alle Identity Pakete ihrer Zielframeworkversion entsprechen

.NET 9 SDK

Note

Die Codebeispiele und Anweisungen für .NET 9 verweisen auf den 10.x-Beispielordner. Der Code ist für dieses Szenario zwischen .NET 9 und .NET 10 identisch. Nur die Zielframework- und Paketversionen unterscheiden sich in der Projektdatei.

Schnellstart: Hinzufügen von zwei benutzerdefinierten Eigenschaften

Für den gängigen Fall beim Hinzufügen von zwei benutzerdefinierten Eigenschaften wie FirstName z. B. und LastName:

  1. Erstellen eines neuen ASP.NET Core Projekts mit der Authentifizierung einzelner Konten
  2. Gerüst Identity zum Überschreiben der Seiten „Registrieren“ und „Verwalten/Index“
  3. Fügen Sie benutzerdefinierte Eigenschaften wie FirstName und LastName zur Benutzerklasse mit dem [PersonalData] Attribut hinzu.
  4. Aktualisieren der Register- und Manage/Index-Seiten, um Eingabefelder für Ihre Eigenschaften einzuschließen
  5. Erstellen und Anwenden einer Migration
  6. Testregistrierung und Profilverwaltung

In den folgenden Abschnitten finden Sie detaillierte schrittweise Anleitungen für diesen Prozess.

Erstellen einer Web-App

Sie können entweder eine Razor Seiten- oder MVC-Web-App erstellen. Beide verwenden die gleiche IdentityRazor Benutzeroberfläche für Seiten.

Razor Seiten

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Web App>weiter aus.
  • Auswählen des Authentifizierungstyps>"Einzelne Konten>erstellen"
  • Erstellen Sie das Projekt, und führen Sie es aus.

MVC

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Web App (Modell-View-Controller)>Weiter
  • Auswählen des Authentifizierungstyps>"Einzelne Konten>erstellen"
  • Erstellen Sie das Projekt, und führen Sie es aus.

Note

Identity Die Benutzeroberfläche wird als Razor Klassenbibliothek implementiert. Wenn Sie ein MVC-Projekt mit Authentifizierung erstellen, werden die Identity-Seiten aus der Razor-Klassenbibliothek bereitgestellt. Das bedeutet, dass Ihr MVC-Projekt Razor-Seiten für Identity verwendet, obwohl im restlichen Projekt MVC-Controller und -Ansichten verwendet werden.

Führen Sie den Identity-Gerüstbau aus.

  • Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >Add>Neues Gerüstelement.
  • Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
  • Wählen Sie im Dialogfeld Identity hinzufügen die folgenden Optionen aus:
    • Auswählen der vorhandenen Layoutdatei ~/Pages/Shared/_Layout.cshtml für Razor Seiten oder ~/Views/Shared/_Layout.cshtml für MVC
    • Wählen Sie die folgenden Dateien zum Überschreiben aus:
      • Account/Register
      • Account/Manage/Index
    • Wählen Sie die + Schaltfläche aus, um eine neue Datenkontextklasse zu erstellen. Übernehmen Sie den Typ (WebApp1.Models.WebApp1Context , wenn das Projekt "WebApp1" heißt).
    • Wählen Sie die + Schaltfläche aus, um eine neue Benutzerklasse zu erstellen. Akzeptieren Sie den Typ (WebApp1User, wenn das Projekt WebApp1 heißt) und klicken Sie auf >.
  • Wählen Sie Hinzufügen aus.

Befolgen Sie die Anweisungen in Migrationen, UseAuthentication und Layout, um die folgenden Schritte auszuführen:

  • Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
  • Fügen Sie UseAuthentication zu Program.cs hinzu
  • Fügen Sie <partial name="_LoginPartial" /> in die Layoutdatei ein.
  • Testen Sie die App:
    • Registrieren eines Benutzers
    • Wählen Sie den neuen Benutzernamen aus (neben dem Link " Abmelden "). Möglicherweise müssen Sie das Fenster erweitern oder das Navigationsleistensymbol auswählen, um den Benutzernamen und andere Links anzuzeigen.
    • Wählen Sie die Registerkarte " Persönliche Daten " aus.
    • Wählen Sie die Schaltfläche "Herunterladen " aus, und überprüfen Sie die PersonalData.json Datei.
    • Testen Sie die Schaltfläche "Löschen ", die den angemeldeten Benutzer löscht.

Hinzufügen von benutzerdefinierten Benutzerdaten zur Identity DB

Aktualisieren Sie die IdentityUser abgeleitete Klasse mit benutzerdefinierten Eigenschaften. Wenn Sie das Projekt WebApp1 benannt haben, wird die Datei benannt Areas/Identity/Data/WebApp1User.cs. Aktualisieren Sie die Datei mit dem folgenden Code:

using Microsoft.AspNetCore.Identity;

namespace WebApp1.Areas.Identity.Data;

public class WebApp1User : IdentityUser
{
    [PersonalData]
    public string? Name { get; set; }
    [PersonalData]
    public DateTime DOB { get; set; }
}

Eigenschaften mit dem PersonalData-Attribut sind:

  • Gelöscht, wenn die Seite Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete aufruft.
  • Sie werden durch die Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor-Seite in die heruntergeladenen Daten einbezogen.

Aktualisieren der Account/Manage/Index.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs mit dem folgenden hervorgehobenen Code:

public class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    /// <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>
    public string Username { get; set; }

    // Remaining API warnings omited.

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    private async Task LoadAsync(WebApp1User user)
    {
        var userName = await _userManager.GetUserNameAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            PhoneNumber = phoneNumber
        };
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        await LoadAsync(user);
        return Page();
    }

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

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

        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        await _userManager.UpdateAsync(user);
        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Manage/Index.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-floating">
                <input asp-for="Username" class="form-control" disabled />
                <label asp-for="Username" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.Name" class="form-control" />
                <label asp-for="Input.Name" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <label asp-for="Input.PhoneNumber" class="form-label"></label>
                <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Aktualisieren der Account/Register.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs mit dem folgenden hervorgehobenen Code:

    public class RegisterModel : PageModel
    {
        private readonly SignInManager<WebApp1User> _signInManager;
        private readonly UserManager<WebApp1User> _userManager;
        private readonly IUserStore<WebApp1User> _userStore;
        private readonly IUserEmailStore<WebApp1User> _emailStore;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;

        public RegisterModel(
            UserManager<WebApp1User> userManager,
            IUserStore<WebApp1User> userStore,
            SignInManager<WebApp1User> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _userStore = userStore;
            _emailStore = GetEmailStore();
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        /// <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>
        [BindProperty]
        public InputModel Input { get; set; }

        // Remaining API warnings omited.
        public string ReturnUrl { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public class InputModel
        {
            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Full name")]
            public string Name { get; set; }

            [Required]
            [Display(Name = "Birth Date")]
            [DataType(DataType.Date)]
            public DateTime DOB { get; set; }

            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }


        public async Task OnGetAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = CreateUser();

                user.Name = Input.Name;
                user.DOB = Input.DOB;

                await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
                await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
                var result = await _userManager.CreateAsync(user, Input.Password);

                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var userId = await _userManager.GetUserIdAsync(user);
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);

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

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

        private WebApp1User CreateUser()
        {
            try
            {
                return Activator.CreateInstance<WebApp1User>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
                    $"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }

        private IUserEmailStore<WebApp1User> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore<WebApp1User>)_userStore;
        }
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Register.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <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.Name" class="form-control" />
                <label asp-for="Input.Name"></label>
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB"></label>
                <span asp-validation-for="Input.DOB" 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>
            <div class="form-floating">
                <input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.Password"></label>
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.ConfirmPassword"></label>
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h3>Use another service to register.</h3>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
                            about setting up this ASP.NET application to support logging in via external services</a>.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins!)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Projekt erstellen.

Aktualisieren des Layouts

Anweisungen zum Hinzufügen von Anmelde- und Abmeldelinks zu jeder Seite finden Sie unter Layoutänderungen .

Hinzufügen einer Migration für die benutzerdefinierten Benutzerdaten

In der Visual Studio Paket-Manager Console:

Add-Migration CustomUserData
Update-Database

Benutzerdefinierte Daten erstellen, anzeigen, herunterladen und löschen testen

Testen der App:

  • Registrieren sie einen neuen Benutzer.
  • Zeigen Sie die benutzerdefinierten Benutzerdaten auf der /Identity/Account/Manage Seite an.
  • Laden Sie die persönlichen Daten der Benutzer von der /Identity/Account/Manage/PersonalData Seite herunter, und zeigen Sie sie an.

Troubleshooting

Wenn Probleme auftreten:

Symptom Wahrscheinliche Ursache Lösung
Benutzerdefinierte Felder werden nicht im Registrierungsformular angezeigt Vergessen, Register.cshtml zu aktualisieren Stellen Sie sicher, dass Sie ein Gerüst Areas/Identity/Pages/Account/Register.cshtml erstellt und geändert haben
Benutzerdefinierte Daten werden nicht gespeichert. Vergessen, eine Migrations- oder Aktualisierungsdatenbank hinzuzufügen Führen Sie dotnet ef migrations add CustomUserData aus und dann dotnet ef database update
Benutzerdefinierte Felder, die nicht in heruntergeladenen Daten enthalten sind Fehlendes [PersonalData] Attribut Hinzufügen eines [PersonalData] Attributs zu Eigenschaften in Ihrer Benutzerklasse
Build-Fehler nach der Gerüsterstellung Paketversionskonflikt Stellen Sie sicher, dass alle Identity Pakete ihrer Zielframeworkversion entsprechen

.NET 8 SDK

Note

Die Codebeispiele und Anweisungen für .NET 8 verweisen auf den 10.x-Beispielordner. Der Code ist für dieses Szenario zwischen .NET 8 und .NET 10 identisch. Nur die Zielframework- und Paketversionen unterscheiden sich in der Projektdatei.

Schnellstart: Hinzufügen von zwei benutzerdefinierten Eigenschaften

Für ein einfaches Szenario, in dem Sie Ihrem FirstName-Benutzer nur einige benutzerdefinierte Eigenschaften wie LastName und Identity hinzufügen möchten:

  1. Erstellen eines neuen ASP.NET Core Projekts mit der Authentifizierung einzelner Konten
  2. Gerüst Identity zum Überschreiben der Seiten „Registrieren“ und „Verwalten/Index“
  3. Hinzufügen ihrer benutzerdefinierten Eigenschaften zur Benutzerklasse (z. B. FirstName, LastName) mit dem [PersonalData] Attribut
  4. Aktualisieren der Register- und Manage/Index-Seiten, um Eingabefelder für Ihre Eigenschaften einzuschließen
  5. Erstellen und Anwenden einer Migration
  6. Testregistrierung und Profilverwaltung

In den folgenden Abschnitten finden Sie detaillierte schrittweise Anleitungen für diesen Prozess.

Erstellen einer Web-App

Sie können entweder eine Razor Seiten- oder MVC-Web-App erstellen. Beide verwenden die gleiche IdentityRazor Benutzeroberfläche für Seiten.

Razor Seiten

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Web App>weiter aus.
  • Auswählen des Authentifizierungstyps>"Einzelne Konten>erstellen"
  • Erstellen Sie das Projekt, und führen Sie es aus.

MVC

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Web App (Modell-View-Controller)>Weiter
  • Auswählen des Authentifizierungstyps>"Einzelne Konten>erstellen"
  • Erstellen Sie das Projekt, und führen Sie es aus.

Note

Identity Die Benutzeroberfläche wird als Razor Klassenbibliothek implementiert. Wenn Sie ein MVC-Projekt mit Authentifizierung erstellen, werden die Identity-Seiten aus der Razor-Klassenbibliothek bereitgestellt. Das bedeutet, dass Ihr MVC-Projekt Razor-Seiten für Identity verwendet, obwohl im restlichen Projekt MVC-Controller und -Ansichten verwendet werden.

Führen Sie den Identity-Gerüstbau aus.

  • Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >Add>Neues Gerüstelement.
  • Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
  • Wählen Sie im Dialogfeld Identity hinzufügen die folgenden Optionen aus:
    • Auswählen der vorhandenen Layoutdatei ~/Pages/Shared/_Layout.cshtml für Razor Seiten oder ~/Views/Shared/_Layout.cshtml für MVC
    • Wählen Sie die folgenden Dateien zum Überschreiben aus:
      • Account/Register
      • Account/Manage/Index
    • Wählen Sie die + Schaltfläche aus, um eine neue Datenkontextklasse zu erstellen. Übernehmen Sie den Typ (WebApp1.Models.WebApp1Context , wenn das Projekt "WebApp1" heißt).
    • Wählen Sie die + Schaltfläche aus, um eine neue Benutzerklasse zu erstellen. Akzeptieren Sie den Typ (WebApp1User, wenn das Projekt WebApp1 heißt) und klicken Sie auf >.
  • Wählen Sie Hinzufügen aus.

Befolgen Sie die Anweisungen in Migrationen, UseAuthentication und Layout, um die folgenden Schritte auszuführen:

  • Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
  • Fügen Sie UseAuthentication zu Program.cs hinzu
  • Fügen Sie <partial name="_LoginPartial" /> in die Layoutdatei ein.
  • Testen Sie die App:
    • Registrieren eines Benutzers
    • Wählen Sie den neuen Benutzernamen aus (neben dem Link " Abmelden "). Möglicherweise müssen Sie das Fenster erweitern oder das Navigationsleistensymbol auswählen, um den Benutzernamen und andere Links anzuzeigen.
    • Wählen Sie die Registerkarte " Persönliche Daten " aus.
    • Wählen Sie die Schaltfläche "Herunterladen " aus, und überprüfen Sie die PersonalData.json Datei.
    • Testen Sie die Schaltfläche "Löschen ", die den angemeldeten Benutzer löscht.

Hinzufügen von benutzerdefinierten Benutzerdaten zur Identity DB

Aktualisieren Sie die IdentityUser abgeleitete Klasse mit benutzerdefinierten Eigenschaften. Wenn Sie das Projekt WebApp1 benannt haben, wird die Datei benannt Areas/Identity/Data/WebApp1User.cs. Aktualisieren Sie die Datei mit dem folgenden Code:

using Microsoft.AspNetCore.Identity;

namespace WebApp1.Areas.Identity.Data;

public class WebApp1User : IdentityUser
{
    [PersonalData]
    public string? Name { get; set; }
    [PersonalData]
    public DateTime DOB { get; set; }
}

Eigenschaften mit dem PersonalData-Attribut sind:

  • Gelöscht, wenn die Seite Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete aufruft.
  • Sie werden durch die Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor-Seite in die heruntergeladenen Daten einbezogen.

Aktualisieren der Account/Manage/Index.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs mit dem folgenden hervorgehobenen Code:

public class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    /// <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>
    public string Username { get; set; }

    // Remaining API warnings omited.

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    private async Task LoadAsync(WebApp1User user)
    {
        var userName = await _userManager.GetUserNameAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            PhoneNumber = phoneNumber
        };
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        await LoadAsync(user);
        return Page();
    }

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

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

        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        await _userManager.UpdateAsync(user);
        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Manage/Index.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-floating">
                <input asp-for="Username" class="form-control" disabled />
                <label asp-for="Username" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.Name" class="form-control" />
                <label asp-for="Input.Name" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <label asp-for="Input.PhoneNumber" class="form-label"></label>
                <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Aktualisieren der Account/Register.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs mit dem folgenden hervorgehobenen Code:

    public class RegisterModel : PageModel
    {
        private readonly SignInManager<WebApp1User> _signInManager;
        private readonly UserManager<WebApp1User> _userManager;
        private readonly IUserStore<WebApp1User> _userStore;
        private readonly IUserEmailStore<WebApp1User> _emailStore;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;

        public RegisterModel(
            UserManager<WebApp1User> userManager,
            IUserStore<WebApp1User> userStore,
            SignInManager<WebApp1User> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _userStore = userStore;
            _emailStore = GetEmailStore();
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        /// <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>
        [BindProperty]
        public InputModel Input { get; set; }

        // Remaining API warnings omited.
        public string ReturnUrl { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public class InputModel
        {
            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Full name")]
            public string Name { get; set; }

            [Required]
            [Display(Name = "Birth Date")]
            [DataType(DataType.Date)]
            public DateTime DOB { get; set; }

            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }


        public async Task OnGetAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = CreateUser();

                user.Name = Input.Name;
                user.DOB = Input.DOB;

                await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
                await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
                var result = await _userManager.CreateAsync(user, Input.Password);

                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var userId = await _userManager.GetUserIdAsync(user);
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);

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

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

        private WebApp1User CreateUser()
        {
            try
            {
                return Activator.CreateInstance<WebApp1User>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
                    $"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }

        private IUserEmailStore<WebApp1User> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore<WebApp1User>)_userStore;
        }
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Register.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <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.Name" class="form-control" />
                <label asp-for="Input.Name"></label>
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB"></label>
                <span asp-validation-for="Input.DOB" 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>
            <div class="form-floating">
                <input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.Password"></label>
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.ConfirmPassword"></label>
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h3>Use another service to register.</h3>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
                            about setting up this ASP.NET application to support logging in via external services</a>.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins!)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Projekt erstellen.

Aktualisieren des Layouts

Anweisungen zum Hinzufügen von Anmelde- und Abmeldelinks zu jeder Seite finden Sie unter Layoutänderungen .

Hinzufügen einer Migration für die benutzerdefinierten Benutzerdaten

In der Visual Studio Paket-Manager Console:

Add-Migration CustomUserData
Update-Database

Benutzerdefinierte Daten erstellen, anzeigen, herunterladen und löschen testen

Testen der App:

  • Registrieren sie einen neuen Benutzer.
  • Zeigen Sie die benutzerdefinierten Benutzerdaten auf der /Identity/Account/Manage Seite an.
  • Laden Sie die persönlichen Daten der Benutzer von der /Identity/Account/Manage/PersonalData Seite herunter, und zeigen Sie sie an.

Troubleshooting

Wenn Probleme auftreten:

Symptom Wahrscheinliche Ursache Lösung
Benutzerdefinierte Felder werden nicht im Registrierungsformular angezeigt Vergessen, Register.cshtml zu aktualisieren Stellen Sie sicher, dass Sie ein Gerüst Areas/Identity/Pages/Account/Register.cshtml erstellt und geändert haben
Benutzerdefinierte Daten werden nicht gespeichert. Vergessen, eine Migrations- oder Aktualisierungsdatenbank hinzuzufügen Führen Sie dotnet ef migrations add CustomUserData aus und dann dotnet ef database update
Benutzerdefinierte Felder, die nicht in heruntergeladenen Daten enthalten sind Fehlendes [PersonalData] Attribut Hinzufügen eines [PersonalData] Attributs zu Eigenschaften in Ihrer Benutzerklasse
Build-Fehler nach der Gerüsterstellung Paketversionskonflikt Stellen Sie sicher, dass alle Identity Pakete ihrer Zielframeworkversion entsprechen

.NET 6 SDK

Erstellen einer Razor Web-App

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Webanwendung>OK aus
  • Webanwendung>OK auswählen
  • Erstellen Sie das Projekt, und führen Sie es aus.

Führen Sie den Identity-Gerüstbau aus.

  • Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >Add>Neues Gerüstelement.
  • Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
  • Wählen Sie im Dialogfeld Identity hinzufügen die folgenden Optionen aus:
    • Auswählen der vorhandenen Layoutdatei ~/Pages/Shared/_Layout.cshtml
    • Wählen Sie die folgenden Dateien zum Überschreiben aus:
      • Account/Register
      • Account/Manage/Index
    • Wählen Sie die + Schaltfläche aus, um eine neue Datenkontextklasse zu erstellen. Übernehmen Sie den Typ (WebApp1.Models.WebApp1Context , wenn das Projekt "WebApp1" heißt).
    • Wählen Sie die + Schaltfläche aus, um eine neue Benutzerklasse zu erstellen. Akzeptieren Sie den Typ (WebApp1User, wenn das Projekt WebApp1 heißt) und klicken Sie auf >.
  • Wählen Sie Hinzufügen aus.

Befolgen Sie die Anweisungen in Migrationen, UseAuthentication und Layout, um die folgenden Schritte auszuführen:

  • Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
  • Fügen Sie UseAuthentication zu Program.cs hinzu
  • Fügen Sie <partial name="_LoginPartial" /> in die Layoutdatei ein.
  • Testen Sie die App:
    • Registrieren eines Benutzers
    • Wählen Sie den neuen Benutzernamen aus (neben dem Link " Abmelden "). Möglicherweise müssen Sie das Fenster erweitern oder das Navigationsleistensymbol auswählen, um den Benutzernamen und andere Links anzuzeigen.
    • Wählen Sie die Registerkarte " Persönliche Daten " aus.
    • Wählen Sie die Schaltfläche "Herunterladen " aus, und überprüfen Sie die PersonalData.json Datei.
    • Testen Sie die Schaltfläche "Löschen ", die den angemeldeten Benutzer löscht.

Hinzufügen von benutzerdefinierten Benutzerdaten zur Identity DB

Aktualisieren Sie die IdentityUser abgeleitete Klasse mit benutzerdefinierten Eigenschaften. Wenn Sie das Projekt WebApp1 benannt haben, wird die Datei benannt Areas/Identity/Data/WebApp1User.cs. Aktualisieren Sie die Datei mit dem folgenden Code:

using Microsoft.AspNetCore.Identity;

namespace WebApp1.Areas.Identity.Data;

public class WebApp1User : IdentityUser
{
    [PersonalData]
    public string? Name { get; set; }
    [PersonalData]
    public DateTime DOB { get; set; }
}

Eigenschaften mit dem PersonalData-Attribut sind:

  • Gelöscht, wenn die Seite Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete aufruft.
  • Sie werden durch die Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor-Seite in die heruntergeladenen Daten einbezogen.

Aktualisieren der Account/Manage/Index.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs mit dem folgenden hervorgehobenen Code:

public class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    /// <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>
    public string Username { get; set; }

    // Remaining API warnings ommited.

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    private async Task LoadAsync(WebApp1User user)
    {
        var userName = await _userManager.GetUserNameAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            PhoneNumber = phoneNumber
        };
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        await LoadAsync(user);
        return Page();
    }

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

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

        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        await _userManager.UpdateAsync(user);
        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Manage/Index.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h3>@ViewData["Title"]</h3>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-floating">
                <input asp-for="Username" class="form-control" disabled />
                <label asp-for="Username" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.Name" class="form-control" />
                <label asp-for="Input.Name" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB" class="form-label"></label>
            </div>
            <div class="form-floating">
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <label asp-for="Input.PhoneNumber" class="form-label"></label>
                <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Aktualisieren der Account/Register.cshtml Seite

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs mit dem folgenden hervorgehobenen Code:

    public class RegisterModel : PageModel
    {
        private readonly SignInManager<WebApp1User> _signInManager;
        private readonly UserManager<WebApp1User> _userManager;
        private readonly IUserStore<WebApp1User> _userStore;
        private readonly IUserEmailStore<WebApp1User> _emailStore;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;

        public RegisterModel(
            UserManager<WebApp1User> userManager,
            IUserStore<WebApp1User> userStore,
            SignInManager<WebApp1User> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _userStore = userStore;
            _emailStore = GetEmailStore();
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        /// <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>
        [BindProperty]
        public InputModel Input { get; set; }

        // Remaining API warnings ommited.
        public string ReturnUrl { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public class InputModel
        {
            [Required]
            [DataType(DataType.Text)]
            [Display(Name = "Full name")]
            public string Name { get; set; }

            [Required]
            [Display(Name = "Birth Date")]
            [DataType(DataType.Date)]
            public DateTime DOB { get; set; }

            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }


        public async Task OnGetAsync(string returnUrl = null)
        {
            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = CreateUser();

                user.Name = Input.Name;
                user.DOB = Input.DOB;

                await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
                await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
                var result = await _userManager.CreateAsync(user, Input.Password);

                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var userId = await _userManager.GetUserIdAsync(user);
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                        protocol: Request.Scheme);

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

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

        private WebApp1User CreateUser()
        {
            try
            {
                return Activator.CreateInstance<WebApp1User>();
            }
            catch
            {
                throw new InvalidOperationException($"Can't create an instance of '{nameof(WebApp1User)}'. " +
                    $"Ensure that '{nameof(WebApp1User)}' is not an abstract class and has a parameterless constructor, or alternatively " +
                    $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml");
            }
        }

        private IUserEmailStore<WebApp1User> GetEmailStore()
        {
            if (!_userManager.SupportsUserEmail)
            {
                throw new NotSupportedException("The default UI requires a user store with email support.");
            }
            return (IUserEmailStore<WebApp1User>)_userStore;
        }
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Register.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <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.Name" class="form-control" />
                <label asp-for="Input.Name"></label>
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.DOB" class="form-control" />
                <label asp-for="Input.DOB"></label>
                <span asp-validation-for="Input.DOB" 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>
            <div class="form-floating">
                <input asp-for="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.Password"></label>
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-floating">
                <input asp-for="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" />
                <label asp-for="Input.ConfirmPassword"></label>
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h3>Use another service to register.</h3>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
                            about setting up this ASP.NET application to support logging in via external services</a>.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins!)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Projekt erstellen.

Aktualisieren des Layouts

Anweisungen zum Hinzufügen von Anmelde- und Abmeldelinks zu jeder Seite finden Sie unter Layoutänderungen .

Hinzufügen einer Migration für die benutzerdefinierten Benutzerdaten

In der Visual Studio Paket-Manager Console:

Add-Migration CustomUserData
Update-Database

Benutzerdefinierte Daten erstellen, anzeigen, herunterladen und löschen testen

Testen der App:

  • Registrieren sie einen neuen Benutzer.
  • Zeigen Sie die benutzerdefinierten Benutzerdaten auf der /Identity/Account/Manage Seite an.
  • Laden Sie die persönlichen Daten der Benutzer von der /Identity/Account/Manage/PersonalData Seite herunter, und zeigen Sie sie an.

.NET Core 3.0 SDK

Erstellen einer Razor Web-App

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Webanwendung>OK aus
  • Webanwendung>OK auswählen
  • Erstellen Sie das Projekt, und führen Sie es aus.

Führen Sie den Identity-Gerüstbau aus.

  • Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >Add>Neues Gerüstelement.
  • Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
  • Wählen Sie im Dialogfeld Identity hinzufügen die folgenden Optionen aus:
    • Auswählen der vorhandenen Layoutdatei ~/Pages/Shared/_Layout.cshtml
    • Wählen Sie die folgenden Dateien zum Überschreiben aus:
      • Account/Register
      • Account/Manage/Index
    • Wählen Sie die + Schaltfläche aus, um eine neue Datenkontextklasse zu erstellen. Übernehmen Sie den Typ (WebApp1.Models.WebApp1Context , wenn das Projekt "WebApp1" heißt).
    • Wählen Sie die + Schaltfläche aus, um eine neue Benutzerklasse zu erstellen. Akzeptieren Sie den Typ (WebApp1User, wenn das Projekt WebApp1 heißt) und klicken Sie auf >.
  • Wählen Sie Hinzufügen aus.

Befolgen Sie die Anweisungen in Migrationen, UseAuthentication und Layout, um die folgenden Schritte auszuführen:

  • Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
  • Füge UseAuthentication zu Startup.Configure hinzu.
  • Fügen Sie <partial name="_LoginPartial" /> in die Layoutdatei ein.
  • Testen Sie die App:
    • Registrieren eines Benutzers
    • Wählen Sie den neuen Benutzernamen aus (neben dem Link " Abmelden "). Möglicherweise müssen Sie das Fenster erweitern oder das Navigationsleistensymbol auswählen, um den Benutzernamen und andere Links anzuzeigen.
    • Wählen Sie die Registerkarte " Persönliche Daten " aus.
    • Wählen Sie die Schaltfläche "Herunterladen " aus, und überprüfen Sie die PersonalData.json Datei.
    • Testen Sie die Schaltfläche "Löschen ", die den angemeldeten Benutzer löscht.

Hinzufügen von benutzerdefinierten Benutzerdaten zur Identity DB

Aktualisieren Sie die IdentityUser abgeleitete Klasse mit benutzerdefinierten Eigenschaften. Wenn Sie das Projekt WebApp1 benannt haben, wird die Datei benannt Areas/Identity/Data/WebApp1User.cs. Aktualisieren Sie die Datei mit dem folgenden Code:

using System;
using Microsoft.AspNetCore.Identity;

namespace WebApp1.Areas.Identity.Data
{
    public class WebApp1User : IdentityUser
    {
        [PersonalData]
        public string Name { get; set; }
        [PersonalData]
        public DateTime DOB { get; set; }
    }
}

Eigenschaften mit dem PersonalData-Attribut sind:

  • Gelöscht, wenn die Seite Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete aufruft.
  • Sie werden durch die Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor-Seite in die heruntergeladenen Daten einbezogen.

Aktualisieren der Seite "Account/Manage/Index.cshtml"

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs mit dem folgenden hervorgehobenen Code:

public partial class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    public string Username { get; set; }

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    private async Task LoadAsync(WebApp1User user)
    {
        var userName = await _userManager.GetUserNameAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            PhoneNumber = phoneNumber
        };
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound(
                $"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        await LoadAsync(user);
        return Page();
    }

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

        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
        if (Input.PhoneNumber != phoneNumber)
        {
            var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, 
                Input.PhoneNumber);

            if (!setPhoneResult.Succeeded)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                throw new InvalidOperationException(
                    $"Unexpected error occurred setting phone number for user with ID '{userId}'.");
            }
        }
        
        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        await _userManager.UpdateAsync(user);

        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Manage/Index.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage" model="Model.StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Username"></label>
                <input asp-for="Username" class="form-control" disabled />
            </div>
            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.PhoneNumber"></label>
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <span asp-validation-for="Input.PhoneNumber" 
                    class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" 
                class="btn btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Aktualisieren der Seite "Account/Register.cshtml"

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs mit dem folgenden hervorgehobenen Code:

[AllowAnonymous]
public class RegisterModel : PageModel
{
    private readonly SignInManager<WebApp1User> _signInManager;
    private readonly UserManager<WebApp1User> _userManager;
    private readonly ILogger<RegisterModel> _logger;
    private readonly IEmailSender _emailSender;

    public RegisterModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager,
        ILogger<RegisterModel> logger,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

    public IList<AuthenticationScheme> ExternalLogins { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

    public async Task OnGetAsync(string returnUrl = null)
    {
        ReturnUrl = returnUrl;
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        if (ModelState.IsValid)
        {
            var user = new WebApp1User {
                Name = Input.Name,
                DOB = Input.DOB,
                UserName = Input.Email, 
                Email = Input.Email 
            };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = user.Id, code = code },
                    protocol: Request.Scheme);

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

                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
                }
                else
                {
                    await _signInManager.SignInAsync(user, isPersistent: false);
                    return LocalRedirect(returnUrl);
                }
            }
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Register.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>

            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
                <span asp-validation-for="Input.DOB" class="text-danger"></span>
            </div>
            
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h4>Use another service to register.</h4>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See 
                             <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                            for details on setting up this ASP.NET application to support 
                            logging in via external services.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" 
                        asp-route-returnUrl="@Model.ReturnUrl" method="post" 
                        class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" 
                                        value="@provider.Name" 
                                        title="Log in using your @provider.DisplayName account">
                                            @provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Projekt erstellen.

Hinzufügen einer Migration für die benutzerdefinierten Benutzerdaten

In der Visual Studio Paket-Manager Console:

Add-Migration CustomUserData
Update-Database

Benutzerdefinierte Daten erstellen, anzeigen, herunterladen und löschen testen

Testen der App:

  • Registrieren sie einen neuen Benutzer.
  • Zeigen Sie die benutzerdefinierten Benutzerdaten auf der /Identity/Account/Manage Seite an.
  • Laden Sie die persönlichen Daten der Benutzer von der /Identity/Account/Manage/PersonalData Seite herunter, und zeigen Sie sie an.

Ansprüche zu Identity mithilfe von IUserClaimsPrincipalFactory<ApplicationUser> hinzufügen

Note

Dieser Abschnitt ist keine Erweiterung des vorherigen Lernprogramms. Informationen zum Anwenden der folgenden Schritte auf die app, die mithilfe des Lernprogramms erstellt wurde, finden Sie unter dieses GitHub problem.

Zusätzliche Claims können zu ASP.NET Core Identity hinzugefügt werden, indem die IUserClaimsPrincipalFactory<T>-Schnittstelle verwendet wird. Diese Klasse kann der App in der Startup.ConfigureServices Methode hinzugefügt werden. Fügen Sie die benutzerdefinierte Implementierung der Klasse wie folgt hinzu:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>,
        AdditionalUserClaimsPrincipalFactory>();

Der Democode verwendet die ApplicationUser Klasse. Diese Klasse fügt eine IsAdmin Eigenschaft hinzu, die zum Hinzufügen des zusätzlichen Anspruchs verwendet wird.

public class ApplicationUser : IdentityUser
{
    public bool IsAdmin { get; set; }
}

AdditionalUserClaimsPrincipalFactory implementiert die UserClaimsPrincipalFactory Schnittstelle. Dem ClaimsPrincipal wird ein neuer Rollenanspruch hinzugefügt.

public class AdditionalUserClaimsPrincipalFactory
        : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public AdditionalUserClaimsPrincipalFactory(
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> optionsAccessor)
        : base(userManager, roleManager, optionsAccessor)
    {}

    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);
        var identity = (ClaimsIdentity)principal.Identity;

        var claims = new List<Claim>();
        if (user.IsAdmin)
        {
            claims.Add(new Claim(JwtClaimTypes.Role, "admin"));
        }
        else
        {
            claims.Add(new Claim(JwtClaimTypes.Role, "user"));
        }

        identity.AddClaims(claims);
        return principal;
    }
}

Der zusätzliche Anspruch kann dann in der App verwendet werden. In einer Razor Seite kann die IAuthorizationService Instanz verwendet werden, um auf den Anspruchswert zuzugreifen.

@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService

@if ((await AuthorizationService.AuthorizeAsync(User, "IsAdmin")).Succeeded)
{
    <ul class="mr-auto navbar-nav">
        <li class="nav-item">
            <a class="nav-link" asp-controller="Admin" asp-action="Index">ADMIN</a>
        </li>
    </ul>
}

.NET Core 2.2 oder höher SDK

Erstellen einer Razor Web-App

  • Wählen Sie im Menü Visual Studio FileNeu>Project aus. Benennen Sie das Projekt WebApp1 , wenn es mit dem Namespace des Downloadbeispielcodes übereinstimmen soll.
  • Wählen Sie ASP.NET Core Webanwendung>OK aus
  • Wählen Sie ASP.NET Core 2.2 in der Dropdownliste aus.
  • Webanwendung>OK auswählen
  • Erstellen Sie das Projekt, und führen Sie es aus.

Führen Sie den Identity-Gerüstbau aus.

  • Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf das Projekt >Add>Neues Gerüstelement.
  • Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
  • Wählen Sie im Dialogfeld Identity hinzufügen die folgenden Optionen aus:
    • Auswählen der vorhandenen Layoutdatei ~/Pages/Shared/_Layout.cshtml
    • Wählen Sie die folgenden Dateien zum Überschreiben aus:
      • Account/Register
      • Account/Manage/Index
    • Wählen Sie die + Schaltfläche aus, um eine neue Datenkontextklasse zu erstellen. Übernehmen Sie den Typ (WebApp1.Models.WebApp1Context , wenn das Projekt "WebApp1" heißt).
    • Wählen Sie die + Schaltfläche aus, um eine neue Benutzerklasse zu erstellen. Akzeptieren Sie den Typ (WebApp1User, wenn das Projekt WebApp1 heißt) und klicken Sie auf >.
  • Wählen Sie Hinzufügen aus.

Befolgen Sie die Anweisungen in Migrationen, UseAuthentication und Layout, um die folgenden Schritte auszuführen:

  • Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
  • Füge UseAuthentication zu Startup.Configure hinzu.
  • Fügen Sie <partial name="_LoginPartial" /> in die Layoutdatei ein.
  • Testen Sie die App:
    • Registrieren eines Benutzers
    • Wählen Sie den neuen Benutzernamen aus (neben dem Link " Abmelden "). Möglicherweise müssen Sie das Fenster erweitern oder das Navigationsleistensymbol auswählen, um den Benutzernamen und andere Links anzuzeigen.
    • Wählen Sie die Registerkarte " Persönliche Daten " aus.
    • Wählen Sie die Schaltfläche "Herunterladen " aus, und überprüfen Sie die PersonalData.json Datei.
    • Testen Sie die Schaltfläche "Löschen ", die den angemeldeten Benutzer löscht.

Hinzufügen von benutzerdefinierten Benutzerdaten zur Identity DB

Aktualisieren Sie die IdentityUser abgeleitete Klasse mit benutzerdefinierten Eigenschaften. Wenn Sie das Projekt WebApp1 benannt haben, wird die Datei benannt Areas/Identity/Data/WebApp1User.cs. Aktualisieren Sie die Datei mit dem folgenden Code:

using Microsoft.AspNetCore.Identity;
using System;

namespace WebApp1.Areas.Identity.Data
{
    public class WebApp1User : IdentityUser
    {
        [PersonalData]
        public string Name { get; set; }
        [PersonalData]
        public DateTime DOB { get; set; }
    }
}

Eigenschaften mit dem PersonalData-Attribut sind:

  • Gelöscht, wenn die Seite Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazorUserManager.Delete aufruft.
  • Sie werden durch die Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor-Seite in die heruntergeladenen Daten einbezogen.

Aktualisieren der Seite "Account/Manage/Index.cshtml"

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs mit dem folgenden hervorgehobenen Code:

public partial class IndexModel : PageModel
{
    private readonly UserManager<WebApp1User> _userManager;
    private readonly SignInManager<WebApp1User> _signInManager;
    private readonly IEmailSender _emailSender;

    public IndexModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
    }

    public string Username { get; set; }
    public bool IsEmailConfirmed { get; set; }

    [TempData]
    public string StatusMessage { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Required]
        [EmailAddress]
        public string Email { get; set; }

        [Phone]
        [Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }

    public async Task<IActionResult> OnGetAsync()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        var userName = await _userManager.GetUserNameAsync(user);
        var email = await _userManager.GetEmailAsync(user);
        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);

        Username = userName;

        Input = new InputModel
        {
            Name = user.Name,
            DOB = user.DOB,
            Email = email,
            PhoneNumber = phoneNumber
        };

        IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);

        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }

        var email = await _userManager.GetEmailAsync(user);
        if (Input.Email != email)
        {
            var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
            if (!setEmailResult.Succeeded)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
            }
        }

        if (Input.Name != user.Name)
        {
            user.Name = Input.Name;
        }

        if (Input.DOB != user.DOB)
        {
            user.DOB = Input.DOB;
        }

        var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
        if (Input.PhoneNumber != phoneNumber)
        {
            var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
            if (!setPhoneResult.Succeeded)
            {
                var userId = await _userManager.GetUserIdAsync(user);
                throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'.");
            }
        }

        await _userManager.UpdateAsync(user);

        await _signInManager.RefreshSignInAsync(user);
        StatusMessage = "Your profile has been updated";
        return RedirectToPage();
    }

    public async Task<IActionResult> OnPostSendVerificationEmailAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var user = await _userManager.GetUserAsync(User);
        if (user == null)
        {
            return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
        }


        var userId = await _userManager.GetUserIdAsync(user);
        var email = await _userManager.GetEmailAsync(user);
        var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
        var callbackUrl = Url.Page(
            "/Account/ConfirmEmail",
            pageHandler: null,
            values: new { userId = userId, code = code },
            protocol: Request.Scheme);
        await _emailSender.SendEmailAsync(
            email,
            "Confirm your email",
            $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

        StatusMessage = "Verification email sent. Please check your email.";
        return RedirectToPage();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Manage/Index.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model IndexModel
@{
    ViewData["Title"] = "Profile";
    ViewData["ActivePage"] = ManageNavPages.Index;
}

<h4>@ViewData["Title"]</h4>
<partial name="_StatusMessage" for="StatusMessage" />
<div class="row">
    <div class="col-md-6">
        <form id="profile-form" method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Username"></label>
                <input asp-for="Username" class="form-control" disabled />
            </div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                @if (Model.IsEmailConfirmed)
                {
                    <div class="input-group">
                        <input asp-for="Input.Email" class="form-control" />
                        <span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
                    </div>
                }
                else
                {
                    <input asp-for="Input.Email" class="form-control" />
                    <button id="email-verification" type="submit" asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button>
                }
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
            </div>
            <div class="form-group">
                <label asp-for="Input.PhoneNumber"></label>
                <input asp-for="Input.PhoneNumber" class="form-control" />
                <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
            </div>
            <button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Aktualisieren der Seite "Account/Register.cshtml"

Aktualisieren Sie das InputModel in Areas/Identity/Pages/Account/Register.cshtml.cs mit dem folgenden hervorgehobenen Code:

[AllowAnonymous]
public class RegisterModel : PageModel
{
    private readonly SignInManager<WebApp1User> _signInManager;
    private readonly UserManager<WebApp1User> _userManager;
    private readonly ILogger<RegisterModel> _logger;
    private readonly IEmailSender _emailSender;

    public RegisterModel(
        UserManager<WebApp1User> userManager,
        SignInManager<WebApp1User> signInManager,
        ILogger<RegisterModel> logger,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

    public class InputModel
    {
        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Full name")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Birth Date")]
        [DataType(DataType.Date)]
        public DateTime DOB { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

    public void OnGet(string returnUrl = null)
    {
        ReturnUrl = returnUrl;
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");
        if (ModelState.IsValid)
        {
            var user = new WebApp1User {
                Name = Input.Name,
                DOB = Input.DOB,
                UserName = Input.Email,
                Email = Input.Email
            };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { userId = user.Id, code = code },
                    protocol: Request.Scheme);

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

                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
}

Aktualisieren Sie das Areas/Identity/Pages/Account/Register.cshtml mit dem unten aufgeführten hervorgehobenen Markup.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>

            <div class="form-group">
                <label asp-for="Input.Name"></label>
                <input asp-for="Input.Name" class="form-control" />
                <span asp-validation-for="Input.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.DOB"></label>
                <input asp-for="Input.DOB" class="form-control" />
                <span asp-validation-for="Input.DOB" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Projekt erstellen.

Hinzufügen einer Migration für die benutzerdefinierten Benutzerdaten

Die benutzerdefinierten Eigenschaften, die der WebApp1UserIdentity Benutzerklasse hinzugefügt werden, müssen in der Datenbank widergespiegelt werden. Eine Migration ist jetzt erforderlich, um die SQL zu generieren, die zum Hinzufügen der neuen Spalten erforderlich ist, und dann muss die Datenbank aktualisiert werden, um diese Änderungen anzuwenden.

Die Identity-Gerüsterstellung hat zusätzlich zum ursprünglichen DbContext (WebApp1Context) neben dem ursprünglichen ApplicationDbContext aus der Projektvorlage erstellt. Sie müssen die neue WebApp1ContextIdentityDbContext angeben, die beim Ausführen von Migrationsbefehlen verwendet werden soll.

In der Visual Studio Paket-Manager Console:

Add-Migration CustomUserData -Context WebApp1Context
Update-Database -Context WebApp1Context

Benutzerdefinierte Daten erstellen, anzeigen, herunterladen und löschen testen

Testen der App:

  • Registrieren sie einen neuen Benutzer.
  • Zeigen Sie die benutzerdefinierten Benutzerdaten auf der /Identity/Account/Manage Seite an.
  • Laden Sie die persönlichen Daten der Benutzer von der /Identity/Account/Manage/PersonalData Seite herunter, und zeigen Sie sie an.