Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Par Rick Anderson
Cet article montre comment :
- Ajoutez des données utilisateur personnalisées à une application web ASP.NET Core.
- Marquez le modèle de données utilisateur personnalisé avec l’attribut PersonalDataAttribute afin qu’il soit automatiquement disponible pour le téléchargement et la suppression. Rendre les données en mesure d’être téléchargées et supprimées permet de répondre aux exigences du RGPD .
L’exemple de projet est créé à partir d’une application web Razor Pages, mais les instructions sont similaires pour une application web MVC ASP.NET Core.
Prerequisites
Afficher ou télécharger l’exemple de code (comment télécharger)
Créer une Razor application web
Créez un projet pour une Razor application web.
Dans Visual Studio, sélectionnez Fichier>Nouveau>Projet.
Entrez un nom pour le projet. Si vous souhaitez que le nom corresponde à l’espace de noms de l’exemple de code de téléchargement , entrez WebApp1.
Sélectionnez ASP.NET Core Application web puis OK.
Dans la liste déroulante Type d’authentification , sélectionnez Comptes individuels.
Sélectionnez Application web , puis OK.
Générez et exécutez le projet.
Exécuter le Identity générateur de modèles automatique
Créez et exécutez le Identity générateur de modèles automatique.
Dans Visual Studio Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Add>New Scaffolded Item.
Dans le volet gauche de la boîte de dialogue Ajouter une structure , sélectionnez Identity>Ajouter.
Dans la boîte de dialogue Ajouter Identity , configurez les options suivantes :
Sélectionnez le fichier de disposition existant, ~/Pages/Shared/_Layout.cshtml.
Sélectionnez les fichiers de page suivants à remplacer :
- Account/Register
- Account/Manage/Index
Sélectionnez l’icône plus (+) et créez une classe de contexte de données. Acceptez le type (par exemple, WebApp1.Models.WebApp1Context si le projet est nommé WebApp1).
Sélectionnez l’icône plus (+) et créez une classe User. Acceptez le type (par exemple , WebApp1User si le projet est nommé WebApp1) et sélectionnez Ajouter.
Pour terminer l’opération, sélectionnez Ajouter.
Créer une migration et vérifier l’application
Après avoir préparé l’échafaudage Identity , créez une migration et vérifiez votre application. Pour connaître les étapes suivantes, consultez les instructions détaillées dans Migrations, UseAuthentication et disposition.
Créez une migration et mettez à jour la base de données.
Ajoutez la
UseAuthenticationméthode au fichier Program.cs .Ajoutez le partiel
<partial name="_LoginPartial" />au fichier de mise en page. Pour plus d’informations, consultez Modifications de disposition.Vérifiez l’application :
Inscrire un utilisateur.
Sélectionnez le nouveau nom d’utilisateur (en regard du lien De déconnexion ). Vous devrez peut-être développer la fenêtre ou sélectionner l’icône de barre de navigation pour afficher le nom d’utilisateur et d’autres liens.
Sélectionnez l’onglet Données personnelles .
Sélectionnez Télécharger et examiner le fichier PersonalData.json .
Sélectionnez Supprimer et confirmez que vous pouvez supprimer l’utilisateur connecté.
Ajouter des données utilisateur personnalisées à la Identity base de données
Vous devez mettre à jour la IdentityUser classe dérivée avec des propriétés personnalisées. Si vous avez nommé le projet WebApp1, le fichier correspondant à mettre à jour est Areas/Identity/Data/WebApp1User.cs. Mettez à jour le fichier avec le code suivant :
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; }
}
Les propriétés avec l’attribut PersonalDataAttribute sont les suivantes :
- Supprimé lorsque la
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor page appelleUserManager.Delete. - Inclus dans les données téléchargées par la
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor page.
Mettre à jour le fichier de page « Account/Manage/Index.cshtml »
Dans le fichier Areas/Identity/Pages/Account/Manage/Index.cshtml.cs , mettez à jour la
InputModeldéfinition de classe avec le code mis en surbrillance suivant :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(); } }Dans le même fichier, mettez à jour les sections indiquées
form-floatingcomme indiqué dans le balisage mis en surbrillance suivant :@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" /> }
Mettre à jour le fichier de page « Account/Register.cshtml »
Dans le fichier Areas/Identity/Pages/Account/Register.cshtml.cs , mettez à jour la
InputModeldéfinition de classe avec le code mis en surbrillance suivant :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; } } }Dans le même fichier, mettez à jour les sections indiquées
form-floatingcomme indiqué dans le balisage mis en surbrillance suivant :@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" /> }Construisez le projet.
Mettre à jour la mise en page
Ajoutez des liens de connexion et de déconnexion à chaque page de l’application web. Pour obtenir des instructions détaillées, consultez Modifications de disposition.
Ajouter une migration pour les données utilisateur personnalisées
Ajoutez des données pour un utilisateur personnalisé à la base de données.
Exécutez les commandes suivantes dans la console Visual Studio Gestionnaire de package :
Add-Migration CustomUserData
Update-Database
Tester l’application avec des données utilisateur personnalisées (créer, afficher, télécharger, supprimer)
Testez votre application web en ajoutant un nouvel utilisateur avec des données personnalisées :
Inscrivez un nouvel utilisateur.
Affichez les données utilisateur personnalisées sur la
/Identity/Account/Managepage.Téléchargez et affichez les données personnelles de l’utilisateur à partir de la
/Identity/Account/Manage/PersonalDatapage.Sélectionnez Supprimer et confirmez que vous pouvez supprimer les données utilisateur personnalisées.
Kit de développement logiciel .NET Core 3.0
Créer une Razor application web
- Dans le menu Visual Studio File, sélectionnez New>Project. Nommez le projet WebApp1 si vous souhaitez qu’il corresponde à l’espace de noms de l’exemple de code de téléchargement .
- Sélectionnez ASP.NET Core application web>OK
- Dans la section Authentification , sélectionnez Comptes d’utilisateur individuels.
- Sélectionnez Application Web>OK
- Générez et exécutez le projet.
Exécuter le Identity générateur de modèles automatique
- À partir de Explorateur de solutions, cliquez avec le bouton droit sur le projet >Ajouter>Nouvel élément échafaudé.
- Dans le volet gauche de la boîte de dialogue Ajouter une structure , sélectionnez Identity>Ajouter.
- Dans la boîte de dialogue Ajouter Identity, les options suivantes :
- Sélectionner le fichier de disposition existant
~/Pages/Shared/_Layout.cshtml - Sélectionnez les fichiers suivants à remplacer :
- Account/Register
- Account/Manage/Index
- Sélectionnez le + bouton pour créer une classe de contexte de données. Acceptez le type (WebApp1.Models.WebApp1Context si le projet est nommé WebApp1).
- Sélectionnez le + bouton pour créer une classe User. Acceptez le type (WebApp1User si le projet est nommé WebApp1) >Ajouter.
- Sélectionner le fichier de disposition existant
- Sélectionnez Ajouter.
Suivez les instructions dans Migrations, UseAuthentication, et la mise en page pour effectuer les étapes suivantes :
- Créez une migration et mettez à jour la base de données.
- Ajout de
UseAuthenticationàStartup.Configure. - Ajouter
<partial name="_LoginPartial" />au fichier de mise en page. - Testez l’application :
- Inscrire un utilisateur
- Sélectionnez le nouveau nom d’utilisateur (en regard du lien De déconnexion ). Vous devrez peut-être développer la fenêtre ou sélectionner l’icône de barre de navigation pour afficher le nom d’utilisateur et d’autres liens.
- Sélectionnez l’onglet Données personnelles .
- Sélectionnez le bouton Télécharger et examinez le
PersonalData.jsonfichier. - Testez le bouton Supprimer , qui supprime l’utilisateur connecté.
Ajouter des données utilisateur personnalisées à la Identity base de données
Mettez à jour la IdentityUser classe dérivée avec des propriétés personnalisées. Si vous avez nommé le projet WebApp1, le fichier est nommé Areas/Identity/Data/WebApp1User.cs.
Mettez à jour le fichier avec le code suivant :
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; }
}
}
Les propriétés avec l’attribut PersonalData sont les suivantes :
- Supprimé lorsque la
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor page appelleUserManager.Delete. - Inclus dans les données téléchargées par la
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor page.
Mettre à jour la page Account/Manage/Index.cshtml
Mettez à jour le InputModel dans Areas/Identity/Pages/Account/Manage/Index.cshtml.cs avec le code mis en surbrillance suivant :
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();
}
}
Mettez à jour le balisage Areas/Identity/Pages/Account/Manage/Index.cshtml mis en surbrillance suivant :
@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" />
}
Mettre à jour la page Account/Register.cshtml
Mettez à jour le InputModel dans Areas/Identity/Pages/Account/Register.cshtml.cs avec le code mis en surbrillance suivant :
[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();
}
}
Mettez à jour le balisage Areas/Identity/Pages/Account/Register.cshtml mis en surbrillance suivant :
@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" />
}
Construisez le projet.
Ajouter une migration pour les données utilisateur personnalisées
Dans la console Visual Studio Gestionnaire de package :
Add-Migration CustomUserData
Update-Database
Testez la création, l'affichage, le téléchargement et la suppression de données utilisateur personnalisées
Testez l’application :
- Inscrivez un nouvel utilisateur.
- Affichez les données utilisateur personnalisées sur la
/Identity/Account/Managepage. - Téléchargez et affichez les données personnelles des utilisateurs à partir de la
/Identity/Account/Manage/PersonalDatapage.
Ajouter des revendications à Identity en utilisant IUserClaimsPrincipalFactory<ApplicationUser>
Note
Cette section n’est pas une extension du didacticiel précédent. Pour appliquer les étapes suivantes à l’application créée à l’aide du didacticiel, consultez ce problème GitHub.
Des revendications supplémentaires peuvent être ajoutées à ASP.NET Core Identity à l’aide de l’interface IUserClaimsPrincipalFactory<T>. Cette classe peut être ajoutée à l’application dans la Startup.ConfigureServices méthode. Ajoutez l’implémentation personnalisée de la classe comme suit :
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>,
AdditionalUserClaimsPrincipalFactory>();
Le code de démonstration utilise la ApplicationUser classe. Cette classe ajoute une IsAdmin propriété utilisée pour ajouter la revendication supplémentaire.
public class ApplicationUser : IdentityUser
{
public bool IsAdmin { get; set; }
}
Le AdditionalUserClaimsPrincipalFactory implémente l’interface UserClaimsPrincipalFactory. Une nouvelle revendication de rôle est ajoutée au ClaimsPrincipal.
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;
}
}
La revendication supplémentaire peut ensuite être utilisée dans l’application. Dans une Razor page, l’instance IAuthorizationService peut être utilisée pour accéder à la valeur de revendication.
@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 Kit de développement logiciel (SDK) Core 2.2 ou version ultérieure
Créer une Razor application web
- Dans le menu Visual Studio File, sélectionnez New>Project. Nommez le projet WebApp1 si vous souhaitez qu’il corresponde à l’espace de noms de l’exemple de code de téléchargement .
- Sélectionnez ASP.NET Core application web>OK
- Sélectionnez ASP.NET Core 2.2 dans la liste déroulante
- Dans la section Authentification , sélectionnez Comptes d’utilisateur individuels.
- Sélectionnez Application Web>OK
- Générez et exécutez le projet.
Exécuter le Identity générateur de modèles automatique
- À partir de Explorateur de solutions, cliquez avec le bouton droit sur le projet >Ajouter>Nouvel élément échafaudé.
- Dans le volet gauche de la boîte de dialogue Ajouter une structure , sélectionnez Identity>Ajouter.
- Dans la boîte de dialogue Ajouter Identity, les options suivantes :
- Sélectionner le fichier de disposition existant
~/Pages/Shared/_Layout.cshtml - Sélectionnez les fichiers suivants à remplacer :
- Account/Register
- Account/Manage/Index
- Sélectionnez le + bouton pour créer une classe de contexte de données. Acceptez le type (WebApp1.Models.WebApp1Context si le projet est nommé WebApp1).
- Sélectionnez le + bouton pour créer une classe User. Acceptez le type (WebApp1User si le projet est nommé WebApp1) >Ajouter.
- Sélectionner le fichier de disposition existant
- Sélectionnez Ajouter.
Suivez les instructions dans Migrations, UseAuthentication, et la mise en page pour effectuer les étapes suivantes :
- Créez une migration et mettez à jour la base de données.
- Ajout de
UseAuthenticationàStartup.Configure. - Ajouter
<partial name="_LoginPartial" />au fichier de mise en page. - Testez l’application :
- Inscrire un utilisateur
- Sélectionnez le nouveau nom d’utilisateur (en regard du lien De déconnexion ). Vous devrez peut-être développer la fenêtre ou sélectionner l’icône de barre de navigation pour afficher le nom d’utilisateur et d’autres liens.
- Sélectionnez l’onglet Données personnelles .
- Sélectionnez le bouton Télécharger et examinez le
PersonalData.jsonfichier. - Testez le bouton Supprimer , qui supprime l’utilisateur connecté.
Ajouter des données utilisateur personnalisées à la Identity base de données
Mettez à jour la IdentityUser classe dérivée avec des propriétés personnalisées. Si vous avez nommé le projet WebApp1, le fichier est nommé Areas/Identity/Data/WebApp1User.cs. Mettez à jour le fichier avec le code suivant :
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; }
}
}
Les propriétés avec l’attribut PersonalData sont les suivantes :
- Supprimé lorsque la
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor page appelleUserManager.Delete. - Inclus dans les données téléchargées par la
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor page.
Mettre à jour la page Account/Manage/Index.cshtml
Mettez à jour le InputModel dans Areas/Identity/Pages/Account/Manage/Index.cshtml.cs avec le code mis en surbrillance suivant :
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();
}
}
Mettez à jour le balisage Areas/Identity/Pages/Account/Manage/Index.cshtml mis en surbrillance suivant :
@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" />
}
Mettre à jour la page Account/Register.cshtml
Mettez à jour le InputModel dans Areas/Identity/Pages/Account/Register.cshtml.cs avec le code mis en surbrillance suivant :
[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();
}
}
Mettez à jour le balisage Areas/Identity/Pages/Account/Register.cshtml mis en surbrillance suivant :
@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" />
}
Construisez le projet.
Ajouter une migration pour les données utilisateur personnalisées
Dans la console Visual Studio Gestionnaire de package :
Add-Migration CustomUserData
Update-Database
Testez la création, l'affichage, le téléchargement et la suppression de données utilisateur personnalisées
Testez l’application :
- Inscrivez un nouvel utilisateur.
- Affichez les données utilisateur personnalisées sur la
/Identity/Account/Managepage. - Téléchargez et affichez les données personnelles des utilisateurs à partir de la
/Identity/Account/Manage/PersonalDatapage.