本文介绍如何:
项目示例将创建从 Razor 页 web 应用,但了 ASP.NET Core MVC web 应用的类似的说明。
dotnet new webapp -o WebApp1
如果以前未安装 ASP.NET Core 基架,请立即进行安装:
dotnet tool install -g dotnet-aspnet-codegenerator
添加到包引用Microsoft.VisualStudio.Web.CodeGeneration.Design项目 (.csproj) 文件。 在项目目录中运行以下命令:
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design dotnet restore
运行以下命令以列出标识基架选项:
dotnet aspnet-codegenerator identity -h
在项目文件夹中,运行标识基架:
dotnet aspnet-codegenerator identity -u WebApp1User -fi Account.Register;Account.Manage.Index
按照中的说明迁移、 UseAuthentication 和布局来执行以下步骤:
UseAuthentication
添加到 Startup.Configure
。<partial name="_LoginPartial" />
布局文件。更新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; } } }
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属性的属性为:
UserManager.Delete
。更新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" /> }
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" /> }
更新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" /> }
[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
dotnet ef migrations add CustomUserData dotnet ef database update
测试应用:
/Identity/Account/Manage
页。/Identity/Account/Manage/PersonalData
页。