定製ASP.NET Core的身份認證

大家好,我是張飛洪,感謝您的閱讀,我會不定期和你分享學習心得,希望我的文章能成爲你成長路上的墊腳石,讓我們一起精進。

在本章,我們將學習如何定製ASP.NET Core認證機制。微軟把安全認證當做ASP.NET Core框架的一部分,足以看見它是應用程序非常最重要的內容。在文將介紹如何定製ASP.NET Core認證UI的基本實現,以及如何向IdentityUser添加自定義信息。我們將介紹以下幾點:

  • 介紹ASP.NET Core身份認證
  • 自定義IdentityUser
  • 自定義Identity視圖(views)
  • 該主題所屬ASP.NET Core架構的MVC層:

技術準備

創建一個ASP.NET Core應用程序並用VS Code打開:

dotnet new mvc -n AuthSample -o AuthSample --auth Individual
cd AuthSample 
code .

ASP.NET Core身份認證介紹

用戶身份是表示用戶對象,也可以是用戶組。通過它我們可以瞭解用戶及其所屬的權限。可以給身份或者叫用戶分配包含權限的角色。例如,可以給一個名爲倉庫管理員的角色只分配寫入的權限。用戶身份也可以嵌套。一個用戶可以是一個組的一部分,一個組可以是另一個組的一部分,依此類推。

ASP.NET Core Identity是一個框架,它在.NET中用來存儲和讀取用戶信息。該框架還提供了添加登錄表單、註冊表單、會話處理等機制。它還提供以加密和安全的方式存儲憑據。ASP.NET Core Identity還提供了多種驗證用戶身份的方法:

  • Individual(個人):應用程序自行管理身份。它有一個存儲用戶信息的數據庫,並自行管理登錄、註銷、註冊等。
  • IndividualB2C:自行管理用戶數據,但從Azure B2C獲取數據。
  • SingleOrg:身份由Azure Active Directory(AD)管理;登錄、註銷等等都是由Azure AD完成的。應用程序只需從web服務器獲取一個隨時可用的身份。
  • 多組織:與上一個相同,但爲多個Azure AD組織啓用。
  • Windows:經典的Windows身份驗證,僅當應用程序由IIS託管時纔可用。用戶還可以從web服務器獲得一個隨時可用的身份。
  • 本文不是關於不同的身份驗證方法,因爲這個主題太大了。

上面命令行裏的--auth標誌設置爲Individual,用於啓用個人身份驗證,並創建ASP.NET Core MVC應用程序。這意味着它附帶了一個數據庫來存儲用戶。--auth標誌會添加所有相關的代碼和依賴項。

--auth標誌創建了一個名爲Identity的區域,其中包含_ViewStart.cshtml文件,它引用新項目的_Layout.cshtml文件。實際的登錄或註冊界面在引用此項目的類庫中提供。

該案例項目包含一個Data文件夾,其中包含EF Core的DbContext,以及用於創建和更新的數據庫遷移。

除了Program.cs外,所有其他部分常規MVC應用完全相同。

如果您使用.NET CLI創建了應用程序,默認則會使用SQLite數據庫。如果使用Visual Studio創建此應用程序,則會使用SQL Server存儲用戶數據。

在啓動應用程序之前,在終端中調用以下命令創建和更新數據庫:

dotnet ef database update

如果不起作用,您可能需要首先在.NET CLI中安裝實體框架工具:

dotnet tool install -g dotnet-ef

然後執行:
dotnet watch
應用程序現在將在監視模式下啓動,並啓用熱重新加載。它還將打開瀏覽器窗口並調用應用程序:

如您所見,右上方有一個菜單,其中包含此應用程序的“註冊”和“登錄”選項。單擊登錄鏈接可進入以下登錄屏幕:

如上所述,該視圖來自一個已編譯的Razor庫,它爲Identity區域提供了必要的視圖。我們會自動從框架中獲取此UI。

最後,我們快速瞭解一下Program.cs,這也與我們在上一章中看到的文件不同。

在註冊服務的上部,有幾行代碼用於註冊DbContext以及數據庫異常頁:

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connectionString)); 
builder.Services.AddDatabaseDeveloperPageExceptionFilter(); 
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)  .AddEntityFrameworkStores<ApplicationDbContext>();

還有添加身份認證的EntityFramework Core的註冊。它被配置爲只允許確認的帳戶,這意味着:作爲用戶需要確認電子郵件才能登錄。
在中間件的下部,我們看到使用了身份驗證和授權:

app.UseAuthentication(); 
app.UseAuthorization();

身份驗證通過讀取身份驗證cookie來識別用戶,它還將所有相關信息添加到Identity對象。
我們可能還需要通過向用戶添加更多屬性來擴展用戶配置文件。我們在下面介紹如何做到這一點。

自定義IdentityUser

IdentityUser具有以下字段:Id、用戶名、密碼、電子郵件和電話號碼。
由於顯示名稱可能與用戶名不同,我們會添加Name屬性,另外,假設我們想向用戶發送生日祝福,我們應該如何擴展他們的出生日期呢?
爲此,需要添加一個名爲WebAppUser.cs的文件到Data文件夾中:

using Microsoft.AspNetCore.Identity; 
namespace AuthSample.Data; 
public class WebAppUser : IdentityUser {     
  [PersonalData]     
  public string? Name { get; set; }     
  [PersonalData]     
  public DateTime DOB { get; set; } 
}

如上所示,WebAppUser.cs派生自IdentityUser,並擴展兩個屬性。
在Program.cs,我們需要修改服務註冊以使用新的WebAppUser:

builder.Services.AddDefaultIdentity<WebAppUser>
我們還需要更改DbContext,使用WebAppUser:

public class ApplicationDbContext :  IdentityDbContext<WebAppUser, IdentityRole, string>

您還需要將using語句添加到Microsoft.AspNetCore.Identity中。這是第一步。我們現在需要更新數據庫:

dotnet ef migrations add CustomUserData 
dotnet ef database update

一旦使用自定義屬性擴展了IdentityUser,就可以在在ASP.NET Core Identity U的用戶配置文件中使用它。

自定義Identity視圖(views)

ASP.NET Core Identity視圖來自編譯的Razor庫,我們應該如何自定義呢?
我們只需要在Area內的預定義文件夾結構中用自定義視圖覆蓋給定視圖即可。前提是我們需要先創建一個自定義視圖,添加擴展字段或更改佈局。
如前所述,項目中已經有一個名爲Identity的Area,內有一個Pages文件夾。在這裏,我們需要創建一個名爲Account的新文件夾,然後放置一個名爲“註冊”的新Register.cshtml頁面在這個文件夾中,並將以下內容放在裏面,以查看視圖的覆蓋是否有效:
@page @{ } <h1>Hello Register Form</h1>
運行應用程序並單擊左上角的註冊,您將看到以下頁面:

實際上,我們不需要自己覆蓋視圖。有一個代碼生成器可用於構建您想要覆蓋的視圖。
通過調用以下命令安裝代碼生成器:
dotnet tool install -g dotnet-aspnet-codegenerator
如果尚未完成,您還需要在項目中安裝以下軟件包:

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design 
dotnet add package Microsoft.EntityFrameworkCore.Design 
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore 
dotnet add package Microsoft.AspNetCore.Identity.UI 
dotnet add package Microsoft.EntityFrameworkCore.SqlServer 
dotnet add package Microsoft.EntityFrameworkCore.Tools

要了解代碼生成器可以做什麼,請運行以下命令:
dotnet aspnet-codegenerator identity -h
第一個更改是讓用戶在註冊頁面上填寫name屬性,我們先構建註冊頁面:

dotnet aspnet-codegenerator identity -dc AuthSample.Data.ApplicationDbContext --files "Account.Register" -sqlite

此命令告訴代碼生成器使用現有的ApplicationDbContext和Sqlite。如果不指定此項,它將創建一個新的DbContext或註冊現有的DbContext,以便與SQLServer而不是SQLite一起使用。

如果一切都OK了,代碼生成器應該只添加Register.cshtml頁面以及一些基礎結構文件:

代碼生成器還會知道項目正在使用自定義WebAppUser而不是IdentityUser,這意味着在生成的代碼中使用了WebAppUser。
現在,我們更改一下Register.cshtml,將顯示名稱添加到表單中。在第15行電子郵件字段的表單元素之前添加以下行:

<div class="form-floating">     
<input asp-for="Input.Name" class="form-control" autocomplete="name" aria-required="true" />
<label asp-for="Input.Name"></label> 
<span asp-validation-for="Input.Name"  class="text-danger"></span> 
</div>

此外,Regiser.cshtml.cs需要更改。ImportModel類需要Name屬性:

public class InputModel {     
[Required]     
[Display(Name = "Display name")]     
public string Name { get; set; }

在PostAsync方法中,將Name屬性分配給新創建的用戶:

var user = CreateUser(); 
user.Name = Input.Name;

啓動申請後,您將看到以下注冊表格:

由於用戶可能需要更新名稱,我們還需要更改配置文件頁面上的視圖。這裏還需要添加出生日期:

dotnet aspnet-codegenerator identity -dc AuthSample.Data.ApplicationDbContext --files "Account.Manage.Index" -sqlite

打開新創建的Index.chtml.cs,並將以下屬性放在InputModel類中:

public class InputModel {     
  [Required]     
  [Display(Name = "Display name")]     
  public string Name { get; set; }     
  [Display(Name = "Date of birth")]     
  public DateTime DOB { get; set; }
}

現在可以在相應的Index.chtml中使用這些屬性。下面代碼片段需要放在驗證和用戶名之間:

<div class="form-floating">     
<input asp-for="Input.Name" class="form-control"  autocomplete="name" aria-required="true" />     
<label asp-for="Input.Name"></label>     
<span asp-validation-for="Input.Name" class="text-danger">
</span> 
</div> 
<div class="form-floating">     
<input asp-for="Input.DOB" class="form-control"  type="date"/>     
<label asp-for="Input.DOB" class="form-label"></label> 
</div>

最後,還需要一些更改才能用保存的數據填充表單。在LoadAsync方法中,需要使用新屬性擴展InputModel的實例化:

Input = new InputModel {     
PhoneNumber = phoneNumber,     
Name = user.Name,     
DOB = user.DOB 
};

用戶保存表單時,還需要保存更改的值。將下一個代碼段放在OnPostAsync方法中:

user.Name = Input.Name; 
user.DOB = Input.DOB; 
await _userManager.UpdateAsync(user);

將InputModel的值設置爲WebAppUser屬性,並將更改保存在數據庫中。
我們在終端中調用dotnet watch來嘗試一下。
Profile頁面現在看起來類似於:

您現在可以更改顯示名稱並添加出生日期。如果用戶填寫了顯示名稱,他們可能會在登錄後在左上角顯示。
打開Views/Shared文件夾的_LoginPartial.cshtml,並將前四行替換爲以下代碼段:

@using Microsoft.AspNetCore.Identity 
@using AuthSample.Data 
@inject SignInManager<WebAppUser> SignInManager 
@inject UserManager<WebAppUser> UserManager 
@{  var user = await @UserManager.GetUserAsync(User); }

做using部分,將SignInManager和UserManager的泛型參數從IdentityUser類型更改爲WebAppUser。
在代碼塊部分,通過傳入當前用戶,通過UserManager加載當前WebAppUser。

現在,需要更改用戶名的輸出以寫入顯示名稱:
Hello @user?.Name!

當dotnet watch仍在運行時,瀏覽器中運行的應用程序應該已經更新。也許你需要重新登錄。現在,您應該會在右上角看到顯示名稱:

總結

在本章中,我們學習瞭如何擴展ASP.NET Core Identity,通過添加其他屬性來增強用戶對象。我們還學習瞭如何增強Identity UI以加載、保存和更新新用戶屬性的值。

但是,應該如何管理應用程序用戶的角色?我們將在下一章中學習的關於配置身份管理的內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章