• (四)用户登录


    ==>>点击查看本系列文章目录

    目录

    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

     启动成功,注册账号:

    完成:

    接下来,我们检查一下后台的用户数据:

     

    完成!

  • 相关阅读:
    2020916 spring总结
    20200915--事务
    20200915-mybatis基础
    20200911--使用注解开发
    20200910--Spring配置
    20200909--spring基础-IOC
    20200909-待补充
    20200909记我所看到的问题
    20200909-spring基础一
    面向对象
  • 原文地址:https://www.cnblogs.com/hongwei918/p/11253064.html
Copyright © 2020-2023  润新知