用户管理及登录页面
Posted hongwei918
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用户管理及登录页面相关的知识,希望对你有一定的参考价值。
目录
1.创建项目
2.重写用户管理界面
3.用户管理
4.用户注册
5.用户登录
6.用户退出
7.自动数据迁移
8.启动应用
1.创建项目
项目中会多出如下红框内的用户身份管理的文件:
单独启动该项目,已经拥有了用户登录、注册、注销、管理等功能页面。
2.重写用户管理界面
但是,我们并不想使用原有的登录页面,我们想要自定义用户页面。
项目=》 右键 =》 添加 =》 新搭建基架的项目
添加过程中如果报错 “运行所选代码生成器时出错”, 则先清理该项目,尝试重新添加,如果仍然报错,重启VSStudio,再次添加基架即可。
然后就会看到项目中添加了下图中的文件:
User.cs 中添加姓名和生日:
public class User : IdentityUser [PersonalData] public string Name get; set; [PersonalData] public DateTime Birthday get; set;
3.用户管理
Leo.Users.Areas.Identity.Pages.Account.Manage.Index.cshtml.cs :
public partial class IndexModel : PageModel private readonly UserManager<User> _userManager; private readonly SignInManager<User> _signInManager; private readonly IEmailSender _emailSender; public IndexModel( UserManager<User> userManager, SignInManager<User> 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 #region 新增 [Required] [DataType(DataType.Text)] [Display(Name = "姓名")] public string Name get; set; [Required] [Display(Name = "生日")] [DataType(DataType.Date)] public DateTime Birthday get; set; #endregion [Required] [EmailAddress] public string Email get; set; [Phone] [Display(Name = "电话")] 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 #region 新增 Name = user.Name, Birthday = user.Birthday, #endregion 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‘."); #region 新增 if (Input.Name != user.Name) user.Name = Input.Name; if (Input.Birthday != user.Birthday) user.Birthday = Input.Birthday; #endregion 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‘."); #region 新增 await _userManager.UpdateAsync(user); #endregion 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();
展示页面 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"> @* 新增 start *@ <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.Birthday"></label> <input asp-for="Input.Birthday" class="form-control" /> </div> @* end *@ <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" />
4.用户注册
Leo.Users.Areas.Identity.Pages.Account.Register.cshtml.cs :
[AllowAnonymous] public class RegisterModel : PageModel private readonly SignInManager<User> _signInManager; private readonly UserManager<User> _userManager; private readonly ILogger<RegisterModel> _logger; private readonly IEmailSender _emailSender; public RegisterModel( UserManager<User> userManager, SignInManager<User> 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 #region 新增 [Required] [DataType(DataType.Text)] [Display(Name = "姓名")] public string Name get; set; [Required] [Display(Name = "生日")] [DataType(DataType.Date)] public DateTime Birthday get; set; #endregion [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 = "密码")] public string Password get; set; [DataType(DataType.Password)] [Display(Name = "确认密码")] [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 User #region 新增 Name = Input.Name, Birthday = Input.Birthday, #endregion 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();
展示页面 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> @* 新增 start *@ <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.Birthday"></label> <input asp-for="Input.Birthday" class="form-control" /> <span asp-validation-for="Input.Birthday" class="text-danger"></span> </div> @* end *@ <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" />
5.用户登录
Login.cshtml.cs :
[AllowAnonymous] public class LoginModel : PageModel private readonly SignInManager<User> _signInManager; private readonly ILogger<LoginModel> _logger; public LoginModel(SignInManager<User> signInManager, ILogger<LoginModel> logger) _signInManager = signInManager; _logger = logger; [BindProperty] public InputModel Input get; set; public IList<AuthenticationScheme> ExternalLogins get; set; public string ReturnUrl get; set; [TempData] public string ErrorMessage get; set; public class InputModel [Required] [EmailAddress] public string Email get; set; [Required] [DataType(DataType.Password)] public string Password get; set; [Display(Name = "Remember me?")] public bool RememberMe get; set; public async Task OnGetAsync(string returnUrl = null) if (!string.IsNullOrEmpty(ErrorMessage)) ModelState.AddModelError(string.Empty, ErrorMessage); returnUrl = returnUrl ?? Url.Content("~/"); // Clear the existing external cookie to ensure a clean login process await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); ReturnUrl = returnUrl; public async Task<IActionResult> OnPostAsync(string returnUrl = null) returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) // This doesn‘t count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); if (result.Succeeded) _logger.LogInformation("User logged in."); return LocalRedirect(returnUrl); if (result.RequiresTwoFactor) return RedirectToPage("./LoginWith2fa", new ReturnUrl = returnUrl, RememberMe = Input.RememberMe ); if (result.IsLockedOut) _logger.LogWarning("User account locked out."); return RedirectToPage("./Lockout"); else ModelState.AddModelError(string.Empty, "Invalid login attempt."); return Page(); // If we got this far, something failed, redisplay form return Page();
Login.cshtml :
@page @model LoginModel @ ViewData["Title"] = "Log in"; <h1>@ViewData["Title"]</h1> <div class="row"> <div class="col-md-4"> <section> <form id="account" method="post"> <h4>Use a local account to log in.</h4> <hr /> <div asp-validation-summary="All" class="text-danger"></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"> <div class="checkbox"> <label asp-for="Input.RememberMe"> <input asp-for="Input.RememberMe" /> @Html.DisplayNameFor(m => m.Input.RememberMe) </label> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Log in</button> </div> <div class="form-group"> <p> <a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a> </p> <p> <a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a> </p> </div> </form> </section> </div> <div class="col-md-6 col-md-offset-2"> <section> <h4>Use another service to log in.</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" />
6.用户退出
Logout.cshtml.cs :
[AllowAnonymous] public class LogoutModel : PageModel private readonly SignInManager<User> _signInManager; private readonly ILogger<LogoutModel> _logger; public LogoutModel(SignInManager<User> signInManager, ILogger<LogoutModel> logger) _signInManager = signInManager; _logger = logger; public void OnGet() public async Task<IActionResult> OnPost(string returnUrl = null) await _signInManager.SignOutAsync(); _logger.LogInformation("User logged out."); if (returnUrl != null) return LocalRedirect(returnUrl); else return Page();
Logout.cshtml :
@page @model LogoutModel @ ViewData["Title"] = "Log out"; <header> <h1>@ViewData["Title"]</h1> <p>您已成功退出</p> </header>
7.自动数据迁移
程序自动完成数据库以及表的构建
如图,会报错,是因为项目中有两个Context 数据上下文:
删掉下面的包含 ApplicationDbContext.cs 的 Data文件夹
然后编译时startup.cs 会报错,找不到ApplicationDbContext 类,此时我们直接将这一段注释掉即可,因为我们添加基架以后已经有了新的数据模块:
public class Startup public Startup(IConfiguration configuration) Configuration = configuration; public IConfiguration Configuration get; // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) services.Configure<CookiePolicyOptions>(options => // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; ); #region 身份认证相关,注释掉 //services.AddDbContext<ApplicationDbContext>(options => // options.UseSqlServer( // Configuration.GetConnectionString("DefaultConnection"))); //services.AddDefaultIdentity<IdentityUser>() // .AddDefaultUI(UIFramework.Bootstrap4) // .AddEntityFrameworkStores<ApplicationDbContext >(); #endregion services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); else app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); #region 身份认证相关,保留 app.UseAuthentication(); #endregion app.UseMvc(routes => routes.MapRoute( name: "default", template: "controller=Home/action=Index/id?"); );
再次添加迁移,不再报错:
Add-Migration CustomUserData
更新到数据库:
Update-Database
完成。
查看数据库:
为了防止以后出现问题,我们删掉原来的DefaultConnection ,将 UsersContextConnection 改为 DefaultConnection, 搜索“UsersContextConnection” ,用到该字符串的地方都替换为 “DefaultConnection” 。
8.启动应用
选择用 Kestrel 服务器启动,方便监控:
启动以后,会抛出异常( InvalidOperationException: No service for type ‘Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]‘ has been registered.):
意思是说 UserManager<IdentityUser> 类型的服务没有被注册,事实上,我们将IdentityUser实现为User ,是对User模型进行管理,搜索 IdentityUser,替换为 User
记得添加命名空间,不然引入的User并不正确:
@inject SignInManager<Leo.Users.Areas.Identity.Data.User> SignInManager @inject UserManager<Leo.Users.Areas.Identity.Data.User> UserManager
启动成功,注册账号:
完成:
接下来,我们检查一下后台的用户数据:
完成!
以上是关于用户管理及登录页面的主要内容,如果未能解决你的问题,请参考以下文章