【从0开始.NET CORE认证】-2 使用.Net Core Identity和EF Core

回顾

朋友们,距离上次从0开始.NET CORE认证-1发布已经过去一周了,上次第一篇文章,其实并没有涉及到Net Core Identity,就是简单的搭了一个项目,让大家对Identity中各种术语有个理解,明白他们出现的位置,已经他们出现能够达到某种功能。以及出现的位置顺序不同,则会出现什么不同的情况。

回顾一下上次写的主要的知识点

  1. Authentication和Authorization 是什么,怎么解释他们
  2. Claim和ClaimType又是什么,能举例子说明吗?
  3. ClaimsIdentity和ClaimsPrincipal的含义是什么,他们的从属关系是什么样的?
  4. app.UseAuthorization()和app.UseAuthentication()的意义是什么,能不能调换?

如果你对上面的问题都能回答,我相信你已经看懂了第一篇我讲了什么。

介绍

本章,我将会正式引入.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包:

  1. Microsoft.AspNetCore.Identity
  2. Microsoft.AspNetCore.Identity.EntityFrameworkCore
  3. Microsoft.EntityFrameworkCore
  4. Microsoft.EntityFrameworkCore.SqlServer
  5. Microsoft.EntityFrameworkCore.Tools

这五个包负责不同的功能,

  1. Microsoft.AspNetCore.Identity //包含AspNetCore.Identity的框架,我们可以使用里面认证授权等功能,最基础的功能
  2. Microsoft.AspNetCore.Identity.EntityFrameworkCore //对AspNetCore.Identity的扩展,让其可以和EF Core配合使用
  3. Microsoft.EntityFrameworkCore //EF Core 这个不用我多说了吧
  4. Microsoft.EntityFrameworkCore.SqlServer //EF Core用来连接sql server的包,如果使用mysql,最后一个换名字就行了
  5. Microsoft.EntityFrameworkCore.Tools //在VS 命令行里面迁移数据需要用的命令 add-migration  update-database

然后安装好这五个包之后,创建一个数据库(这个数据库可以是专门存放用户信息的数据库,也可以是跟业务耦合在一起的数据库)因为这个只是关于用户管理的示例项目,所以没有业务。所以我会创建一个AppUserDb的数据库,在项目中的appsetting.json中会配置好连接字符串

链接字符串的格式

Server=.;Database=TBS;User ID=AppUserDb;Password=Dd112233;Trusted_Connection=False;MultipleActiveResultSets=true

具体操作看图-安装NuGet包

创建数据库和配置连接字符串

 

配置EF Core

我们先开始在项目下面创建一个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增加下列代码

具体操作看图:

配置.Net Core Identity

我们在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的用户访问被保护的接口时候跳转到这个Api
5             });
在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 : Controller
10     {
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内配置一下

 

 

 

将Identity和EF Core结合起来

还记得我们安装了一个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;

现在运行一下我们的项目,能够正常运行,然后访问被保护的资源的时候会提示跳转到登录页。

具体看图

CURD用户

准备工作

微软很贴心的为我们准备了两个类,一个是负责管理用户信息的,一个是负责用户登录、登出转换权限的

  1. UserManager<T>
  2. SignInManager<T>

从字面意思也能看出来,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                 else
18                 {
19                     return View("Index");
20                 }
21             }
22             else
23                 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                 else
13                     return Ok("失败");
14             }
15             else
16                 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                 else
12                     return Ok("失败");
13             }
14             return Ok("user is not existed");
15         }
RemoveUser

 

操作示意图如下

 

 

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

可以看到,非常成功。有了.Net Core Identity配合EF Core就不需要我们自己去写一套用户管理逻辑了。

总结

  1. 学习了怎么使用.Net Core Identity和EF Core绑定使用
  2. 学习了两个基本类UserManager和SiginManager
  3. 学习了增删改用户信息

问题

  1. 如果我删除用户的时候,我把我自己删除了,然后我还能继续访问需要授权的页面吗?
  2. 更新个人信息的时候,更新成功了,Cookies会变吗?
  3. 隐藏小BUG,在注册用户的时候,会提示一个错误,并且需要修改一处代码!你能找到它吗?

又写完一篇,决定上传项目

gitee地址:https://gitee.com/JiMoKongTingChunYuWan_admin/IdentityDemo

github地址:https://github.com/Mrlie/IdentityDemo.git

下周见

posted @ 2020-01-06 17:30  寂寞空庭春欲晚  阅读(1577)  评论(2编辑  收藏  举报