• 【从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

    下周见

  • 相关阅读:
    很简单的企业管理器我写程序的方式,几个自定义控件。
    当OO遇到了持久化?!
    [自定义服务器控件] 第一步:文本框。
    [面向过程——老酒换新瓶] (一)开篇:是面向过程还是面向对象?
    个人理财小助手 —— 设计思路、功能说明
    《Head First 设计模式》 终于出中文版了。
    其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感
    基类、接口的应用——表单控件:一次添加、修改一条记录,一次修改多条记录。(上)
    其实添加数据也可以这样简单——表单的第三步抽象(针对UI及后置代码)
    转帖:客户端表单通用验证checkForm(oForm) js版
  • 原文地址:https://www.cnblogs.com/lihuadeblog/p/12153351.html
Copyright © 2020-2023  润新知