zoukankan      html  css  js  c++  java
  • Asp.Net Core Identity 完成注册登录

     Identity是Asp.Net Core全新的一个用户管理系统,它是一个完善的全面的庞大的框架,提供的功能有:

    • 创建、查询、更改、删除账户信息

    • 验证和授权

    • 密码重置

    • 双重身份认证

    • 支持扩展登录,如微软、Facebook、google、QQ、微信等

    • 提供了一个丰富的API,并且这些API还可以进行大量的扩展

        接下来我们先来看下它的简单使用。首先在我们的DbContext中需要继承自IdentityDbContext。

        public class AppDbContext:IdentityDbContext
        {
    
            public AppDbContext(DbContextOptions<AppDbContext> options):base(options)
            {
            }
    
            public DbSet<Student> Students { get; set; }
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.Seed();
            }
        }

        然后在Startup中注入其依赖,IdentityUser和IdentityRole是Identity框架自带的两个类,将其绑定到我们定义的AppDbContext中。

    services.AddIdentity<IdentityUser, IdentityRole>()
        .AddEntityFrameworkStores<AppDbContext>();

        最后需要添加中间件UseAuthentication。

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        //如果环境是Development,调用 Developer Exception Page
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
        app.UseStatusCodePages();
        app.UseStatusCodePagesWithReExecute("/Error/{0}");
        }
        app.UseStaticFiles();
    
        app.UseAuthentication();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
        });
    }

        接下来我们就可以使用数据库迁移 Add-Migration 来添加迁移,然后update-database我们的数据库。

        可以在数据库中看到其生成的表。

        在完成数据迁移之后,我们再来看下Identity中如何完成用户的注册和登录。

        我们定义一个ViewModel,然后定义一个AccountController来完成我们的注册和登录功能。Asp.Net Core Identity为我们提供了UserManger来对用户进行增删改等操作,提供了SignInManager的SignInAsync来登录,SignOutAsync来退出,IsSignedIn来判断用户是否已登录等。

    public class RegisterViewModel
    {
        [Required]
        [Display(Name = "邮箱地址")]
        [EmailAddress]
        public string Email { get; set; }
    
        [Required]
        [Display(Name = "密码")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    
        [DataType(DataType.Password)]
        [Display(Name = "确认密码")]
        [Compare("Password",
            ErrorMessage = "密码与确认密码不一致,请重新输入.")]
        public string ConfirmPassword { get; set; }
    }
    public class LoginViewModel
    {
    
        [Required]
        [EmailAddress]
        public string Email { get; set; }
    
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    
        [Display(Name = "记住我")]
        public bool RememberMe { get; set; }
    }
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using StudentManagement.ViewModels;
    using System.Threading.Tasks;
    
    namespace StudentManagement.Controllers
    {
        public class AccountController:Controller
        {
            private UserManager<IdentityUser> userManager;
            private SignInManager<IdentityUser> signInManager;
    
            public AccountController(UserManager<IdentityUser> userManager,
               SignInManager<IdentityUser> signInManager)
            {
                this.userManager = userManager;
                this.signInManager = signInManager;
            }
    
            [HttpGet]
            public IActionResult Register()
            {
                return View();
            }
    
            [HttpPost]
            public async Task<IActionResult> Register(RegisterViewModel model)
            {
                if (ModelState.IsValid)
                {
                    //将数据从RegisterViewModel复制到IdentityUser
                    var user = new IdentityUser
                    {
                        UserName = model.Email,
                        Email = model.Email
                    };
    
                    //将用户数据存储在AspNetUsers数据库表中
                    var result = await userManager.CreateAsync(user, model.Password);
                
                    //如果成功创建用户,则使用登录服务登录用户信息
                    //并重定向到home econtroller的索引操作
                    if (result.Succeeded)
                    {
                        await signInManager.SignInAsync(user, isPersistent: false);
                        return RedirectToAction("index", "home");
                    }
    
                    //如果有任何错误,将它们添加到ModelState对象中
                    //将由验证摘要标记助手显示到视图中
                    foreach (var error in result.Errors)
                    {
                        if (error.Code== "PasswordRequiresUpper")
                        {
                            error.Description = "密码必须至少有一个大写字母('A'-'Z')。";
                        }
    
                        //PasswordRequiresUpper
                        //Passwords must have at least one uppercase ('A'-'Z').
                        ModelState.AddModelError(string.Empty, error.Description);
                    }
                }
                return View(model);
            }
    
            [HttpPost]
            public async Task<IActionResult> Logout()
            {
                await signInManager.SignOutAsync();
                return RedirectToAction("index", "home");
            }
    
            [HttpGet]
            [AllowAnonymous]
            public IActionResult Login()
            {
                return View();
            }
    
            [HttpPost]
            [AllowAnonymous]
            public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
            {
                if (ModelState.IsValid)
                {
                    var result = await signInManager.PasswordSignInAsync(
                        model.Email, model.Password, model.RememberMe, false);
                    if (result.Succeeded)
                    {
                        if (!string.IsNullOrEmpty(returnUrl))
                        {
                            return Redirect(returnUrl);
                        }
                        else
                        {
                            return RedirectToAction("index", "home");
                        }
                    }
                    ModelState.AddModelError(string.Empty, "登录失败,请重试");
                }
                return View(model);
            }
        }
    }
    @model RegisterViewModel
    
    @{
        ViewBag.Title = "用户注册";
    }
    
    <h1>用户注册</h1>
    <div class="row">
        <div class="col-md-12">
            <form method="post">
                <div asp-validation-summary="All" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="Email"></label>
                    <input asp-for="Email" class="form-control" />
                    <span asp-validation-for="Email" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Password"></label>
                    <input asp-for="Password" class="form-control" />
                    <span asp-validation-for="Password" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="ConfirmPassword"></label>
                    <input asp-for="ConfirmPassword" class="form-control" />
                    <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
                </div>
                <button type="submit" class="btn btn-primary">注册</button>
            </form>
        </div>
    </div>
    @model LoginViewModel
    
    @{
        ViewBag.Title = "用户登录";
    }
    
    <h1>用户登录</h1>
    
    <div class="row">
        <div class="col-md-12">
            <form method="post">
                <div asp-validation-summary="All" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="Email"></label>
                    <input asp-for="Email" class="form-control" />
                    <span asp-validation-for="Email" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Password"></label>
                    <input asp-for="Password" class="form-control" />
                    <span asp-validation-for="Password" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <div class="checkbox">
                        <label asp-for="RememberMe">
                            <input asp-for="RememberMe" />
                            @Html.DisplayNameFor(m => m.RememberMe)
                        </label>
                    </div>
                </div>
                <button type="submit" class="btn btn-primary">登录</button>
            </form>
        </div>
    </div>

        在实际工作中,我们需要配置密码的复杂度来增强用户信息的安全性。而Asp.Net Core Identity也默认也提供了一套机制PasswordOptions,可以查看其源码。

    https://github.com/aspnet/AspNetCore/blob/master/src/Identity/Extensions.Core/src/PasswordOptions.cs

        但是有时候我们需要自定义我们的密码校验模式,这时候可以在Startup中注入

    services.Configure<IdentityOptions>(options =>
    {
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 3;
        options.Password.RequireUppercase = false;
        options.Password.RequireLowercase = false;
        options.Password.RequireNonAlphanumeric = false;
    });

        同时,我们希望我们在注册的时候提示错误信息时使用中文显示,可以定义一个继承IdentityErrorDescriber的类。

    using Microsoft.AspNetCore.Identity;
    
    namespace StudentManagement.Middleware
    {
        public class CustomIdentityErrorDescriber : IdentityErrorDescriber
        {
            public override IdentityError DefaultError()
            {
                return new IdentityError { Code = nameof(DefaultError), Description = $"发生了未知的故障。" };
            }
    
            public override IdentityError ConcurrencyFailure()
            {
                return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "乐观并发失败,对象已被修改。" };
            }
    
            public override IdentityError PasswordMismatch()
            {
                return new IdentityError { Code = nameof(PasswordMismatch), Description = "密码错误" };
            }
    
            public override IdentityError InvalidToken()
            {
                return new IdentityError { Code = nameof(InvalidToken), Description = "无效的令牌." };
            }
    
            public override IdentityError LoginAlreadyAssociated()
            {
                return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "具有此登录的用户已经存在." };
            }
    
            public override IdentityError InvalidUserName(string userName)
            {
                return new IdentityError { Code = nameof(InvalidUserName), Description = $"用户名'{userName}'无效,只能包含字母或数字." };
            }
    
            public override IdentityError InvalidEmail(string email)
            {
                return new IdentityError { Code = nameof(InvalidEmail), Description = $"Email '{email}' is invalid." };
            }
    
            public override IdentityError DuplicateUserName(string userName)
            {
                return new IdentityError { Code = nameof(DuplicateUserName), Description = $"User Name '{userName}' is already taken." };
            }
    
            public override IdentityError DuplicateEmail(string email)
            {
                return new IdentityError { Code = nameof(DuplicateEmail), Description = $"Email '{email}' is already taken." };
            }
    
            public override IdentityError InvalidRoleName(string role)
            {
                return new IdentityError { Code = nameof(InvalidRoleName), Description = $"Role name '{role}' is invalid." };
            }
    
            public override IdentityError DuplicateRoleName(string role)
            {
                return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"Role name '{role}' is already taken." };
            }
    
            public override IdentityError UserAlreadyHasPassword()
            {
                return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "User already has a password set." };
            }
    
            public override IdentityError UserLockoutNotEnabled()
            {
                return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "Lockout is not enabled for this user." };
            }
    
            public override IdentityError UserAlreadyInRole(string role)
            {
                return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"User already in role '{role}'." };
            }
    
            public override IdentityError UserNotInRole(string role)
            {
                return new IdentityError { Code = nameof(UserNotInRole), Description = $"User is not in role '{role}'." };
            }
    
            public override IdentityError PasswordTooShort(int length)
            {
                return new IdentityError { Code = nameof(PasswordTooShort), Description = $"密码必须至少是{length}字符." };
            }
    
            public override IdentityError PasswordRequiresNonAlphanumeric()
            {
                return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "密码必须至少有一个非字母数字字符."
                };
            }
    
            public override IdentityError PasswordRequiresDigit()
            {
                return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = $"密码必须至少有一个数字('0'-'9')." };
            }
    
    
            public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
            {
                return new IdentityError { Code = nameof(PasswordRequiresUniqueChars), Description = $"密码必须使用至少不同的{uniqueChars}字符。" };
            }
    
            public override IdentityError PasswordRequiresLower()
            {
                return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "密码必须至少有一个小写字母('a'-'z')." };
            }
    
            public override IdentityError PasswordRequiresUpper()
            {
                return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "密码必须至少有一个大写字母('A'-'Z')." };
            }
        }
    }

        最后需要在注入Identity的时候添加上这个类

    services.AddIdentity<IdentityUser, IdentityRole>()
        .AddErrorDescriber<CustomIdentityErrorDescriber>()
        .AddEntityFrameworkStores<AppDbContext>();

        完成登录后,我们需要对访问资源进行授权,需要在controller或者action上使用Authorize属性来标记,也可以使用AllowAnonymous来允许匿名访问,在项目中使用授权需要引入中间件UseAuthentication

     app.UseAuthentication();

        但是如果项目中有很多controller需要添加Authorize属性,我们可以在startup中添加全局的授权,代码如下。

    services.AddMvc(config => {
        var policy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
    });

        一般在用户登录成功后需要重定向到原始的 URL,这个通过请求参数中带returnUrl来实现,但是如果没有判断是否本地的Url时则会引发开放式重定向漏洞。

        解决开放式重定向漏洞的方式也很简单,就是在判断的时候添加Url.IsLocalUrl或者直接return LocalRedirect。

    if (Url.IsLocalUrl(returnUrl))
    {
    
    }
    return LocalRedirect(returnUrl);
  • 相关阅读:
    2015-12-25-(菜单栏的效果)
    2015-12-23-(实现左边题目,右边内容的效果)
    2015-12-23-( dispaly:table的用法)
    2015-12-21(box-sizing:border-box)
    闭包
    js中this的用法
    jQuery或Angular实现弹出框单击显示,双击隐藏
    jQuery 点击任意处隐藏,除某个元素外
    导航栏滚动到顶部后固定
    h5 audio标签在手机上不能自动播放????
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11562040.html
Copyright © 2011-2022 走看看