回顧
朋友們,距離上次從0開始.NET CORE認證-1發布已經過去一周了,上次第一篇文章,其實並沒有涉及到Net Core Identity,就是簡單的搭了一個項目,讓大家對Identity中各種術語有個理解,明白他們出現的位置,已經他們出現能夠達到某種功能。以及出現的位置順序不同,則會出現什么不同的情況。
回顧一下上次寫的主要的知識點
- Authentication和Authorization 是什么,怎么解釋他們
- Claim和ClaimType又是什么,能舉例子說明嗎?
- ClaimsIdentity和ClaimsPrincipal的含義是什么,他們的從屬關系是什么樣的?
- 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 }
具體操作看下面的圖
然后我們創建一個控制器名為HomeController,里面定義兩個Action,一個為Index,另外一個為Secert。也創建兩個視圖。我們可以直接復制BasiclyIdentity的視圖和控制器,並且運行。查看能否正常運行。
具體操作請看圖(
tips:一個解決方案內有多個可啟動項目,注意調試的時候選擇正確的項目):
調試,發現可以成功運行了。那我們可以開展下一步了
配置框架
我們將會使用.Net Core Identity和EF Core幫助我們管理用戶,他並不會默認自帶在我們創建的項目中
所以我們需要引入五個Nuget包:
- Microsoft.AspNetCore.Identity
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
這五個包負責不同的功能,
- Microsoft.AspNetCore.Identity //包含AspNetCore.Identity的框架,我們可以使用里面認證授權等功能,最基礎的功能
- Microsoft.AspNetCore.Identity.EntityFrameworkCore //對AspNetCore.Identity的擴展,讓其可以和EF Core配合使用
- Microsoft.EntityFrameworkCore //EF Core 這個不用我多說了吧
- Microsoft.EntityFrameworkCore.SqlServer //EF Core用來連接sql server的包,如果使用mysql,最后一個換名字就行了
- 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 }

1 services.AddDbContext<AppDbContext>(config => 2 { 3 config.UseSqlServer(configuration.GetSection("ConnectionString").Value, opt => 4 { 5 //opt.CommandTimeout(6000); 6 }); 7 });
具體操作看圖:
配置.Net Core Identity
我們在StartUp.cs文件中引用.Net Core Identity,其中IdentityUser和IdentityRole是框架內置的用戶類和角色類,如果我們需要做擴展,只需要繼承此類即可
主要是以下代碼

1 services.AddIdentity<IdentityUser, IdentityRole>() 2 .AddDefaultTokenProviders();
你認為到此結束了嗎?其實還沒有,在第一篇文章中,我們使用了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 });
具體操作看下圖:
同理,我們既然指定了當用戶沒有授權的時候,要跳轉到/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 }

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>

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>
操作請看圖
好了,現在我們想一想我們的准備工作還差什么,我們已經有了登錄、注冊頁面,然后認證方式也選了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用戶
准備工作
微軟很貼心的為我們准備了兩個類,一個是負責管理用戶信息的,一個是負責用戶登錄、登出轉換權限的
- UserManager<T>
- 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 }
操作示意圖如下
登錄處理
一個用戶需要登錄,我們最簡單的登錄需要知道用戶的用戶名和密碼,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 }
操作示意圖如下
注冊處理
一個用戶需要注冊,我們可能需要很多信息,但是最重要的也就是賬號和密碼這是我們必須要收集的,我們在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 }
操作示意圖如下
修改用戶
修改用戶我們必須先拿到這個用戶,然后去修改,所以肯定會傳遞過來一個要修改用戶的主鍵,我們在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 }
然后增加一個頁面

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>
代碼如下

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 }
操作示意圖如下
刪除用戶
跟修改用戶一樣,肯定是拿到主鍵才能刪除,所以我演示成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 }
然后增加一個頁面

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

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 }
操作示意圖如下
至此所有工作都准備好,測試一下。
可以看到,非常成功。有了.Net Core Identity配合EF Core就不需要我們自己去寫一套用戶管理邏輯了。
總結
- 學習了怎么使用.Net Core Identity和EF Core綁定使用
- 學習了兩個基本類UserManager和SiginManager
- 學習了增刪改用戶信息
問題
- 如果我刪除用戶的時候,我把我自己刪除了,然后我還能繼續訪問需要授權的頁面嗎?
- 更新個人信息的時候,更新成功了,Cookies會變嗎?
- 隱藏小BUG,在注冊用戶的時候,會提示一個錯誤,並且需要修改一處代碼!你能找到它嗎?
又寫完一篇,決定上傳項目
gitee地址:https://gitee.com/JiMoKongTingChunYuWan_admin/IdentityDemo
github地址:https://github.com/Mrlie/IdentityDemo.git
下周見