用户管理及登录页面

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

 启动成功,注册账号:

技术图片

完成:

技术图片

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

 技术图片

完成!

 

以上是关于用户管理及登录页面的主要内容,如果未能解决你的问题,请参考以下文章

学生管理App测试用例

开源私有云平台Nano v0.7.1发布 - 新增用户权限管理及系统初始化

Linux下查看/管理当前登录用户及用户操作历史记录

Linux下查看/管理当前登录用户及用户操作历史记录

MongoDB基础之登录验证及用户管理

Modx 管理器自定义页面检查用户是不是已登录并具有管理器访问权限