朋友们,距离上次从0开始.NET CORE认证-1发布已经过去一周了,上次第一篇文章,其实并没有涉及到Net Core Identity,就是简单的搭了一个项目,让大家对Identity中各种术语有个理解,明白他们出现的位置,已经他们出现能够达到某种功能。以及出现的位置顺序不同,则会出现什么不同的情况。
回顾一下上次写的主要的知识点
如果你对上面的问题都能回答,我相信你已经看懂了第一篇我讲了什么。
本章,我将会正式引入.Net Core Identity,然后还会引入EF Core,将.Net Core Identity的用户数据通过EF Core持久化到数据库中,用大白话就是把用户数据保存到数据库,以下思维导图的部分就是我们要做的部分

本文含有大量GIF图,请耐心等待加载
我们继续使用第一篇文章中的解决方案,然后右键——添加——新建项目,选择ASP.NET CORE Web 应用程序,项目名称我们取为:AspNetCoreIdentityExample
同样,我们需要MVC框架来帮助我们搭建前端页面,所以修改一下StartUp.cs的内容,可以直接从上个项目BasiclyIdentity中复制一个基本代码,使其修改成这样。

1 public class Startup 2 { 3 public void ConfigureServices(IServiceCollection services) 4 { 5 services.AddControllersWithViews(); 6 } 7 8 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 9 {10 if (env.IsDevelopment())11 {12 app.UseDeveloperExceptionPage();13 }14 15 app.UseRouting();16 17 app.UseEndpoints(endpoints =>18 {19 endpoints.MapDefaultControllerRoute();20 });21 }22 }
StartUp.cs
具体操作看下面的图

然后我们创建一个控制器名为HomeController,里面定义两个Action,一个为Index,另外一个为Secert。也创建两个视图。我们可以直接复制BasiclyIdentity的视图和控制器,并且运行。查看能否正常运行。
具体操作请看图(
tips:一个解决方案内有多个可启动项目,注意调试的时候选择正确的项目):

调试,发现可以成功运行了。那我们可以开展下一步了
我们将会使用.Net Core Identity和EF Core帮助我们管理用户,他并不会默认自带在我们创建的项目中
所以我们需要引入五个Nuget包:
这五个包负责不同的功能,
然后安装好这五个包之后,创建一个数据库(这个数据库可以是专门存放用户信息的数据库,也可以是跟业务耦合在一起的数据库)因为这个只是关于用户管理的示例项目,所以没有业务。所以我会创建一个AppUserDb的数据库,在项目中的appsetting.json中会配置好连接字符串
链接字符串的格式
Server=.;Database=TBS;User ID=AppUserDb;Password=Dd112233;Trusted_Connection=False;MultipleActiveResultSets=true
具体操作看图-安装NuGet包

创建数据库和配置连接字符串
我们先开始在项目下面创建一个Data文件夹,然后创建数据库上下文AppDbContext.cs,让其继承DbContext,然后在StartUp.cs中注入这个上下文,随后执行迁移命令,通过EF Core在数据库内生成相应的表。
创建上下文,并注入。主要是以下代码

1 using Microsoft.EntityFrameworkCore; 2 3 namespace AspNetCoreIdentityExample.Data 4 { 5 public class AppDbContext:DbContext 6 { 7 public AppDbContext(DbContextOptions<AppDbContext> options) 8 :base(options) 9 {10 11 }12 }13 }
AppDbContext.cs 
1 services.AddDbContext<AppDbContext>(config =>2 {3 config.UseSqlServer(configuration.GetSection("ConnectionString").Value, opt =>4 {5 //opt.CommandTimeout(6000);6 });7 });
在ConfigureServices增加下列代码
具体操作看图:

我们在StartUp.cs文件中引用.Net Core Identity,其中IdentityUser和IdentityRole是框架内置的用户类和角色类,如果我们需要做扩展,只需要继承此类即可
主要是以下代码

1 services.AddIdentity<IdentityUser, IdentityRole>()2 .AddDefaultTokenProviders();
在ConfigureServices增加下列代码

你认为到此结束了吗?其实还没有,在第一篇文章中,我们使用了Cookie作为认证授权的条件,但是我们这里好像没有指定什么东西作为登录授权的条件。所以我们也要为.Net Core Identity开启Cookies认证(.Net Core Identity不仅仅支持Cookie,此话后文再说)

1 services.ConfigureApplicationCookie(config =>2 {3 config.Cookie.Name = ".NetCoreIdentity.Cookies";4 config.LoginPath = "/Home/Login"; //让没有Cookie的用户访问被保护的接口时候跳转到这个Api5 });
在ConfigureServices增加下列代码
具体操作看下图:

同理,我们既然指定了当用户没有授权的时候,要跳转到/Home/Login,所以我们要新增两个页面一个是登录页,一个是注册页
在HomeController里面增加两个Action,使其变成下面的代码,然后增加两个页面一个是Login.cshtml一个是Register.cshtml,代码如下

1 using Microsoft.AspNetCore.Authentication; 2 using Microsoft.AspNetCore.Authorization; 3 using Microsoft.AspNetCore.Mvc; 4 using System.Collections.Generic; 5 using System.Security.Claims; 6 7 namespace AspNetCoreIdentityExample.Controllers 8 { 9 public class HomeController : Controller10 {11 public IActionResult Index()12 {13 return View("Index");14 }15 16 [Authorize]17 public IActionResult Secert()18 {19 return View("Secert");20 }21 22 [HttpGet]23 public IActionResult Login()24 {25 return View("Login");26 }27 28 [HttpGet]29 public IActionResult Register()30 {31 return View("Register");32 }33 }34 }
HomeController
1 <h1>登录页</h1>2 <form method="post" formaction="/Home/Login">3 <input name="username" type="text" value="" />4 <input name="password" type="password" value="" />5 <button>登录</button>6 </form>7 <a href="/Home/Register">没有账号去注册</a>
Login.cshtml
1 <h1>注册页</h1>2 <form method="post" formaction="/Home/Register">3 <input name="username" type="text" value="" />4 <input name="password" type="password" value="" />5 <button>注册</button>6 </form>7 <a href="/Home/Login">已有账号去登陆</a>
Register.cshtml
操作请看图

好了,现在我们想一想我们的准备工作还差什么,我们已经有了登录、注册页面,然后认证方式也选了Cookie,我们也配置了数据库。
所以,我们目前还少了将登录、注册的业务逻辑、将用户数据持久化到数据库的代码。还有一个最重要的就是调用UseAuthorization()和UseAuthentication()方法,如果你不用这个方法,当访问带有[Authorize]标签的控制器的时候,就会出错,所以我们在StartUp.cs内配置一下

还记得我们安装了一个Microsoft.AspNetCore.Identity.EntityFrameworkCore的包吗?微软给我们已经写好了一个关于Identity的数据库上下文,让我们直接继承这个上下文就可以在EF Core中使用Identity。
所以我们修改一下,打开AppDbContext.cs将本来继承DbContext的,更改成IdentityDbContext

然后我们执行以下数据库迁移(EF Core 一般都是写代码,然后把代码中的类通过tools迁移到数据库,就不需要手动设置数据库了)。
在程序包控制台执行以下命令
1 Add-Migration InitUserDb -c AppDbContext -o AppMigration/User然后执行
1 update-database这样数据库就迁移成功了。具体操作看图

现在数据库也生成了,我们还需要在StartUp.cs将两个关系绑定起来

上面两个是孤立的,我们要增加一个方法改成下图

在认证中开启EntityFramworkStores;
现在运行一下我们的项目,能够正常运行,然后访问被保护的资源的时候会提示跳转到登录页。
具体看图

微软很贴心的为我们准备了两个类,一个是负责管理用户信息的,一个是负责用户登录、登出转换权限的
从字面意思也能看出来,UserManager是管理用户的,SiginManager是处理用户登录登出的,我们要在HomeController注入它们,然后使用它们,<T>是指用户类型,如果你有个类继承了IdentityUser这个类,那么你应该传入你自定义的类,否则传IdentityUser即可
代码如下

1 private readonly UserManager<IdentityUser> _userManager;2 private readonly SignInManager<IdentityUser> _signInManager;3 4 public HomeController(UserManager<IdentityUser> userManager,SignInManager<IdentityUser> signInManager)5 {6 _userManager = userManager;7 _signInManager = signInManager;8 }
注入UserManager和SiginManager
操作示意图如下
一个用户需要登录,我们最简单的登录需要知道用户的用户名和密码,so,我们在Home控制器下创建一个action叫做Login,指定参数username和password,如果登录成功就跳转到/Home/Secert页面
代码如下

1 [HttpPost] 2 public async Task<IActionResult> Login(string username, string password) 3 { 4 var user = await _userManager.FindByNameAsync(username).ConfigureAwait(false); 5 if (user != null) 6 { 7 var signResult = await _signInManager.PasswordSignInAsync(user, password, false, false).ConfigureAwait(false); 8 if (signResult.Succeeded) 9 {10 return View("Secert");11 }12 }13 return View("Index");14 }
Login
操作示意图如下

一个用户需要注册,我们可能需要很多信息,但是最重要的也就是账号和密码这是我们必须要收集的,我们在Home控制器下创建一个action叫做Register,指定参数username和password,如果注册成功,那么我们就默认其已经登录,然后就跳转到/Home/Secert页面
代码如下

1 [HttpPost] 2 public async Task<IActionResult> Register(string username, string password) 3 { 4 var user = new IdentityUser 5 { 6 UserName = username, 7 Email = "lihua@qq.com", 8 }; 9 var createResult = await _userManager.CreateAsync(user, password);10 if (createResult.Succeeded)11 {12 var signResult = await _signInManager.PasswordSignInAsync(user, password, false, false);13 if (signResult.Succeeded)14 {15 return View("Index");16 }17 else18 {19 return View("Index");20 }21 }22 else23 return View("Register");24 }
Register
操作示意图如下

修改用户我们必须先拿到这个用户,然后去修改,所以肯定会传递过来一个要修改用户的主键,我们在Home控制器下创建一个action叫做Update,这里我就演示成通过用户名修改,
增加一个个人信息页面名称为Update.cshtml
代码如下

1 [Authorize]2 [HttpGet]3 public async Task<IActionResult> Update()4 {5 var user = await _userManager.GetUserAsync(HttpContext.User);6 ViewBag.Curs = user;7 return View("Update");8 }
在HomeController增加一个Action
然后增加一个页面

1 <h1>修改个人信息</h1>2 <form formaction="/Home/Update" method="post">3 <input name="username" value="@ViewBag.Curs.UserName" />4 <input name="email" value="@ViewBag.Curs.Email" />5 <button>确认修改</button>6 </form>
Update.cshtml
代码如下

1 [Authorize] 2 [HttpPost] 3 public async Task<IActionResult> Update(string username,string email) 4 { 5 var user = await _userManager.FindByNameAsync(username); 6 if (user != null) 7 { 8 user.Email = email; 9 var result = await _userManager.UpdateAsync(user);10 if (result.Succeeded)11 return RedirectToAction("Update");12 else13 return Ok("失败");14 }15 else16 return Ok("user is not existed");17 }
Update
操作示意图如下

跟修改用户一样,肯定是拿到主键才能删除,所以我演示成username作为主键拿到用户
代码如下

1 [Authorize]2 [HttpGet]3 public async Task<IActionResult> RemoveUser()4 {5 var user = await _userManager.GetUserAsync(HttpContext.User);6 ViewBag.Curs = user;7 return View("Remove");8 }
在HomeController增加一个Action
然后增加一个页面

1 <h1>删除信息</h1>2 <form formaction="/Home/RemoveUser" method="post">3 <input name="username" value="@ViewBag.Curs.UserName" />4 <button>确认删除</button>5 </form>
RemoveUser.cshtml

1 [Authorize] 2 [HttpPost] 3 public async Task<IActionResult> RemoveUser(string username) 4 { 5 var user = await _userManager.FindByNameAsync(username); 6 if (user != null) 7 { 8 var result = await _userManager.DeleteAsync(user); 9 if(result.Succeeded)10 return RedirectToAction("Index");11 else12 return Ok("失败");13 }14 return Ok("user is not existed");15 }
RemoveUser
操作示意图如下

至此所有工作都准备好,测试一下。

可以看到,非常成功。有了.Net Core Identity配合EF Core就不需要我们自己去写一套用户管理逻辑了。
又写完一篇,决定上传项目
gitee地址:https://gitee.com/JiMoKongTingChunYuWan_admin/IdentityDemo
github地址:https://github.com/Mrlie/IdentityDemo.git
下周见