Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Автор: Рик Андерсон (Rick Anderson)
В этой статье показано, как сделать следующее:
- Добавление пользовательских данных в веб-приложение ASP.NET Core.
- Пометьте пользовательскую модель данных с атрибутом PersonalDataAttribute , чтобы она была автоматически доступна для скачивания и удаления. Благодаря тому, что данные могут быть скачаны и удалены, соответствуют требованиям GDPR .
Пример проекта создается из веб-приложения Razor Pages, но инструкции аналогичны для веб-приложения ASP.NET Core MVC.
Prerequisites
пакет SDK .NET 6
Просмотр или скачивание примера кода (как скачать)
Razor Создание веб-приложения
Создайте проект для Razor веб-приложения.
В Visual Studio выберите File>New>Project.
Введите имя проекта. Если вы хотите, чтобы имя соответствовало пространству имен примера кода скачивания , введите WebApp1.
Выберите ASP.NET Core веб-приложение а затем выберите OK.
В раскрывающемся списке типов проверки подлинности выберите отдельные учетные записи.
Выберите веб-приложение и нажмите кнопку "ОК".
Соберите проект и запустите его.
Запустите генератор Identity
Создайте и запустите Identity scaffolder.
В Visual Studio Обозреватель решений щелкните проект правой кнопкой мыши и выберите Add>New Scaffolded Item.
В левой области диалогового окна "Добавить шаблон " нажмите кнопку Identity>"Добавить".
В диалоговом окне "Добавление" Identity настройте следующие параметры:
Выберите существующий файл макета ~/Pages/Shared/_Layout.cshtml.
Выберите следующие файлы страниц, которые нужно переопределить:
- Account/Register
- Account/Manage/Index
Выберите значок плюса (+) и создайте класс контекста данных. Примите тип (например, WebApp1.Models.WebApp1Context , если проект называется WebApp1).
Выберите значок плюса (+) и создайте новый класс User. Примите тип (например, WebApp1User , если проект называется WebApp1) и нажмите кнопку "Добавить".
Чтобы завершить операцию, нажмите кнопку "Добавить".
Создание миграции и проверка приложения
После подготовки шаблонов Identity создайте миграцию и проверьте приложение. Инструкции для следующих шагов см. в разделе «Миграции», «UseAuthentication» и «Макет».
Создайте миграцию и обновите базу данных.
Добавьте метод в
UseAuthenticationфайл Program.cs .Добавьте часть
<partial name="_LoginPartial" />в файл макета. Дополнительные сведения см. в разделе "Изменения макета".Проверьте приложение:
Зарегистрируйте пользователя.
Выберите новое имя пользователя (рядом со ссылкой "Выход "). Может потребоваться развернуть окно или выбрать значок панели навигации, чтобы отобразить имя пользователя и другие ссылки.
Перейдите на вкладку "Персональные данные ".
Выберите "Скачать " и проверьте файл PersonalData.json .
Выберите Удалить и подтвердите, что вы можете удалить вошедшего в систему пользователя.
Добавление пользовательских данных пользователя в Identity базу данных
Необходимо обновить производный класс IdentityUser, добавив пользовательские свойства. Если вы назвали проект WebApp1, соответствующий файл для обновления — "Области/Identityданные/WebApp1User.cs". Обновите файл со следующим кодом:
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; }
}
Свойства с атрибутом PersonalDataAttribute :
- Удаляется, когда страница
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor вызываетUserManager.Delete. - Данные, загруженные со страницы
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor, включены.
Обновление файла страницы Account/Manage/Index.cshtml
В файле Areas/Identity/Pages/Account/Manage/Index.cshtml.cs обновите определение класса
InputModelследующим выделенным фрагментом кода: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(); } }В том же файле обновите указанные
form-floatingразделы, как показано в следующей выделенной разметке:@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" /> }
Обновление файла страницы Account/Register.cshtml
В файле Areas/Identity/Pages/Account/Register.cshtml.cs измените определение класса
InputModel, добавив следующий выделенный код: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; } } }В том же файле обновите указанные
form-floatingразделы, как показано в следующей выделенной разметке:@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" /> }Создайте проект.
Обновление макета
Добавьте ссылки на вход и выход на каждую страницу в веб-приложении. Подробные инструкции см. в разделе " Изменения макета".
Добавить миграцию для кастомных данных пользователя
Добавьте данные для настраиваемого пользователя в базу данных.
Выполните следующие команды в консоли Visual Studio диспетчер пакетов:
Add-Migration CustomUserData
Update-Database
Тестирование приложения с пользовательскими данными пользователя (создание, просмотр, скачивание, удаление)
Протестируйте веб-приложение, добавив нового пользователя с пользовательскими данными:
Зарегистрируйте нового пользователя.
Просмотр пользовательских данных пользователя на
/Identity/Account/Manageстранице.Скачайте и просмотрите персональные данные пользователя на
/Identity/Account/Manage/PersonalDataстранице.Выберите "Удалить " и подтвердите, что вы можете удалить пользовательские данные пользователя.
пакет SDK .NET Core 3.0
Razor Создание веб-приложения
- В меню Visual Studio File выберите New>Project. Присвойте проекту имя WebApp1 , если оно соответствует пространству имен скачиваемого примера кода.
- Выберите ASP.NET Core веб-приложение>OK
- В разделе "Проверка подлинности" выберите отдельные учетные записи пользователей.
- Выбор веб-приложения>ОК
- Соберите проект и запустите его.
Запустите генератор Identity
- В Обозреватель решений щелкните правой кнопкой мыши на проекте и выберите >Добавить>Новый элемент с каркасом.
- В левой области диалогового окна "Добавить шаблон " нажмите кнопку Identity>"Добавить".
- В диалоговом окне "Добавление" Identity доступны следующие параметры:
- Выберите существующий файл макета
~/Pages/Shared/_Layout.cshtml - Выберите следующие файлы, чтобы заменить:
- Account/Register
- Account/Manage/Index
- Нажмите кнопку + , чтобы создать новый класс контекста данных. Примите тип (WebApp1.Models.WebApp1Context , если проект называется WebApp1).
- Нажмите кнопку + , чтобы создать новый класс User. Примите тип (WebApp1User , если проект называется WebApp1) >Add.
- Выберите существующий файл макета
- Нажмите кнопку "Добавить".
Выполните инструкции в разделе "Миграции", "UseAuthentication" и "Макет", чтобы выполнить следующие действия:
- Создайте миграцию и обновите базу данных.
- Добавьте
UseAuthenticationвStartup.Configure. - Добавьте
<partial name="_LoginPartial" />в файл макета. - Протестируйте приложение:
- Регистрация пользователя
- Выберите новое имя пользователя (рядом со ссылкой "Выход "). Может потребоваться развернуть окно или выбрать значок панели навигации, чтобы отобразить имя пользователя и другие ссылки.
- Перейдите на вкладку "Персональные данные ".
- Нажмите кнопку "Скачать " и осмотрите
PersonalData.jsonфайл. - Проверьте кнопку "Удалить ", которая удаляет пользователя, вошедшего в систему.
Добавьте пользовательские данные в Identity базу данных
Обновите производный IdentityUser класс с помощью настраиваемых свойств. Если вы назвали проект WebApp1, то файл называется Areas/Identity/Data/WebApp1User.cs.
Обновите файл со следующим кодом:
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; }
}
}
Свойства с атрибутом PersonalData :
- Удаляется, когда страница
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor вызываетUserManager.Delete. - Данные, загруженные со страницы
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor, включены.
Обновление страницы Account/Manage/Index.cshtml
Обновите InputModel в Areas/Identity/Pages/Account/Manage/Index.cshtml.cs с помощью следующего выделенного кода:
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();
}
}
Обновите Areas/Identity/Pages/Account/Manage/Index.cshtml с использованием следующей выделенной разметки:
@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" />
}
Обновление страницы Account/Register.cshtml
Обновите InputModel в Areas/Identity/Pages/Account/Register.cshtml.cs с помощью следующего выделенного кода:
[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();
}
}
Обновите Areas/Identity/Pages/Account/Register.cshtml с использованием следующей выделенной разметки:
@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" />
}
Создайте проект.
Добавить миграцию для кастомных данных пользователя
В консоли Visual Studio диспетчер пакетов:
Add-Migration CustomUserData
Update-Database
Тестирование создания, просмотра, скачивания, удаления пользовательских данных пользователя
Проверьте работу приложения:
- Зарегистрируйте нового пользователя.
- Просмотр пользовательских данных пользователя на
/Identity/Account/Manageстранице. - Скачайте и просмотрите персональные данные пользователей на
/Identity/Account/Manage/PersonalDataстранице.
Добавьте утверждения в Identity, используя IUserClaimsPrincipalFactory<ApplicationUser>
Note
Этот раздел не является расширением предыдущего руководства. Чтобы применить следующие шаги к приложению, созданному с помощью руководства, см. статью проблема GitHub.
Дополнительные утверждения можно добавить в ASP.NET Core Identity с помощью интерфейса IUserClaimsPrincipalFactory<T>. Этот класс можно добавить в приложение в методе Startup.ConfigureServices . Добавьте пользовательскую реализацию класса следующим образом:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>,
AdditionalUserClaimsPrincipalFactory>();
Демонстрационный код использует класс ApplicationUser. Этот класс добавляет IsAdmin свойство, которое используется для добавления дополнительного утверждения.
public class ApplicationUser : IdentityUser
{
public bool IsAdmin { get; set; }
}
AdditionalUserClaimsPrincipalFactory реализует интерфейс UserClaimsPrincipalFactory. В приложение 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;
}
}
Затем в приложении можно использовать дополнительное утверждение. На странице Razor можно использовать экземпляр IAuthorizationService для получения значения заявления.
@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>
}
SDK .NET Core 2.2 или более поздней версии
Razor Создание веб-приложения
- В меню Visual Studio File выберите New>Project. Присвойте проекту имя WebApp1 , если оно соответствует пространству имен скачиваемого примера кода.
- Выберите ASP.NET Core веб-приложение>OK
- Выберите ASP.NET Core 2.2 в раскрывающемся списке
- В разделе "Проверка подлинности" выберите отдельные учетные записи пользователей.
- Выбор веб-приложения>ОК
- Соберите проект и запустите его.
Запустите генератор Identity
- В Обозреватель решений щелкните правой кнопкой мыши на проекте и выберите >Добавить>Новый элемент с каркасом.
- В левой области диалогового окна "Добавить шаблон " нажмите кнопку Identity>"Добавить".
- В диалоговом окне "Добавление" Identity доступны следующие параметры:
- Выберите существующий файл макета
~/Pages/Shared/_Layout.cshtml - Выберите следующие файлы, чтобы заменить:
- Account/Register
- Account/Manage/Index
- Нажмите кнопку + , чтобы создать новый класс контекста данных. Примите тип (WebApp1.Models.WebApp1Context , если проект называется WebApp1).
- Нажмите кнопку + , чтобы создать новый класс User. Примите тип (WebApp1User , если проект называется WebApp1) >Add.
- Выберите существующий файл макета
- Нажмите кнопку "Добавить".
Выполните инструкции в разделе "Миграции", "UseAuthentication" и "Макет", чтобы выполнить следующие действия:
- Создайте миграцию и обновите базу данных.
- Добавьте
UseAuthenticationвStartup.Configure. - Добавьте
<partial name="_LoginPartial" />в файл макета. - Протестируйте приложение:
- Регистрация пользователя
- Выберите новое имя пользователя (рядом со ссылкой "Выход "). Может потребоваться развернуть окно или выбрать значок панели навигации, чтобы отобразить имя пользователя и другие ссылки.
- Перейдите на вкладку "Персональные данные ".
- Нажмите кнопку "Скачать " и осмотрите
PersonalData.jsonфайл. - Проверьте кнопку "Удалить ", которая удаляет пользователя, вошедшего в систему.
Добавьте пользовательские данные в Identity базу данных
Обновите производный IdentityUser класс с помощью настраиваемых свойств. Если вы назвали проект WebApp1, то файл называется Areas/Identity/Data/WebApp1User.cs. Обновите файл со следующим кодом:
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; }
}
}
Свойства с атрибутом PersonalData :
- Удаляется, когда страница
Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtmlRazor вызываетUserManager.Delete. - Данные, загруженные со страницы
Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtmlRazor, включены.
Обновление страницы Account/Manage/Index.cshtml
Обновите InputModel в Areas/Identity/Pages/Account/Manage/Index.cshtml.cs с помощью следующего выделенного кода:
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();
}
}
Обновите Areas/Identity/Pages/Account/Manage/Index.cshtml с использованием следующей выделенной разметки:
@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" />
}
Обновление страницы Account/Register.cshtml
Обновите InputModel в Areas/Identity/Pages/Account/Register.cshtml.cs с помощью следующего выделенного кода:
[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();
}
}
Обновите Areas/Identity/Pages/Account/Register.cshtml с использованием следующей выделенной разметки:
@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" />
}
Создайте проект.
Добавить миграцию для кастомных данных пользователя
В консоли Visual Studio диспетчер пакетов:
Add-Migration CustomUserData
Update-Database
Тестирование создания, просмотра, скачивания, удаления пользовательских данных пользователя
Проверьте работу приложения:
- Зарегистрируйте нового пользователя.
- Просмотр пользовательских данных пользователя на
/Identity/Account/Manageстранице. - Скачайте и просмотрите персональные данные пользователей на
/Identity/Account/Manage/PersonalDataстранице.
Связанный контент
ASP.NET Core