Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Von Rick Anderson
In diesem Artikel werden die folgenden Aufgaben erläutert:
- 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 Seiten-Web-App erstellt, aber die Anweisungen sind für eine ASP.NET Core MVC-Web-App ähnlich.
Anzeigen oder Herunterladen von Beispielcode (Anleitung zum Herunterladen)
Prerequisites
Erstellen einer Razor Web-App
- Klicken Sie in Visual Studio im Menü Datei auf Neu>Projekt. Benennen Sie das Projekt WebApp1 , wenn sie dem Namespace des Downloadbeispielcodes entsprechen 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 das Identity Gerüst aus
- Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie dann >Hinzufügen>Neues Gerüstelement aus.
- Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
- Im Dialogfeld "HinzufügenIdentity " werden die folgenden Optionen angezeigt:
- 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 >.
- Auswählen der vorhandenen Layoutdatei
- Wählen Sie Hinzufügen aus.
Führen Sie die Anweisungen in Migrationen, UseAuthentication und Layout aus, um die folgenden Schritte auszuführen:
- Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
- Fügen Sie
UseAuthenticationzuProgram.cshinzu - 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.jsonDatei. - 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.Deleteaufruft. - In die heruntergeladenen Daten von der
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor Seite eingeschlossen.
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 Package Manager-Konsole:
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/ManageSeite an. - Laden Sie die persönlichen Daten der Benutzer von der
/Identity/Account/Manage/PersonalDataSeite herunter, und zeigen Sie sie an.
Erstellen einer Razor Web-App
- Klicken Sie in Visual Studio im Menü Datei auf Neu>Projekt. Benennen Sie das Projekt WebApp1 , wenn sie dem Namespace des Downloadbeispielcodes entsprechen 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 das Identity Gerüst aus
- Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie dann >Hinzufügen>Neues Gerüstelement aus.
- Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
- Im Dialogfeld "HinzufügenIdentity " werden die folgenden Optionen angezeigt:
- 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 >.
- Auswählen der vorhandenen Layoutdatei
- Wählen Sie Hinzufügen aus.
Führen Sie die Anweisungen in Migrationen, UseAuthentication und Layout aus, um die folgenden Schritte auszuführen:
- Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
-
UseAuthenticationwurdeStartup.Configurehinzugefügt. - 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.jsonDatei. - 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.Deleteaufruft. - In die heruntergeladenen Daten von der
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor Seite eingeschlossen.
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 Package Manager-Konsole:
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/ManageSeite an. - Laden Sie die persönlichen Daten der Benutzer von der
/Identity/Account/Manage/PersonalDataSeite 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 mit dem Lernprogramm erstellt wurde, finden Sie in diesem GitHub-Problem.
Zusätzliche Ansprüche können mithilfe der Identity Schnittstelle zu ASP.NET Core IUserClaimsPrincipalFactory<T> hinzugefügt werden. 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. Ein neuer Rollenanspruch wird dem ClaimsPrincipal 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>
}
Erstellen einer Razor Web-App
- Klicken Sie in Visual Studio im Menü Datei auf Neu>Projekt. Benennen Sie das Projekt WebApp1 , wenn sie dem Namespace des Downloadbeispielcodes entsprechen 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 das Identity Gerüst aus
- Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie dann >Hinzufügen>Neues Gerüstelement aus.
- Wählen Sie im linken Bereich des Dialogfelds " Gerüst hinzufügen " die Option Identity>"Hinzufügen" aus.
- Im Dialogfeld "HinzufügenIdentity " werden die folgenden Optionen angezeigt:
- 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 >.
- Auswählen der vorhandenen Layoutdatei
- Wählen Sie Hinzufügen aus.
Führen Sie die Anweisungen in Migrationen, UseAuthentication und Layout aus, um die folgenden Schritte auszuführen:
- Erstellen Sie eine Migration, und aktualisieren Sie die Datenbank.
-
UseAuthenticationwurdeStartup.Configurehinzugefügt. - 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.jsonDatei. - 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.Deleteaufruft. - In die heruntergeladenen Daten von der
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor Seite eingeschlossen.
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
In der Visual Studio Package Manager-Konsole:
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/ManageSeite an. - Laden Sie die persönlichen Daten der Benutzer von der
/Identity/Account/Manage/PersonalDataSeite herunter, und zeigen Sie sie an.