深入解讀 ASP.NET Core 身份認證過程
阿新 • • 發佈:2020-10-05
長話短說:上文我們講了 ASP.NET Core 基於宣告的訪問控制到底是什麼鬼?
今天我們乘勝追擊:聊一聊ASP.NET Core 中的身份驗證。
> 身份驗證是確定使用者身份的過程。 授權是確定使用者是否有權訪問資源的過程。
### 1. 萬變不離其宗
顯而易見,一個常規的身份認證用例包括兩部分:
① 對使用者進行身份驗證
② 在未經身份驗證的使用者試圖訪問受限資源時作出反應
已註冊的身份驗證處理程式及其配置選項被稱為“方案”,方案可用作一種機制,供使用者參考相關處理程式的身份驗證、挑戰和禁止行為。
> 我們口頭上常說的:
基於cookie認證方案,若認證成功,go on,若認證失敗則跳轉回登入頁面;
基於基本身份認證(BA)方案,若認證成功,go on,若認證失敗則給瀏覽器返回`WWW-Authenticate`標頭, 瀏覽器會再次彈出認證視窗。
### 2. ASP.NET Core認證原理
在 ASP.NET Core 中,身份驗證由`IAuthenticationService`負責,身份驗證服務會呼叫已註冊的身份驗證處理程式來完成與身份驗證相關的操作, 整個驗證過程由`認證中介軟體`來串聯。
一圖以蔽之:
![](https://imgkr2.cn-bj.ufileos.com/c99ce9c3-22f9-43da-958a-92c281838c02.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=7tOjcM%252B2V8NpeVRkXw1g4De8VcU%253D&Expires=1601817858)
其中有幾個關鍵步驟
1. 認證處理程式
可結合方案Scheme中的配置項`AuthenticationSchemeOptions`編寫認證處理程式。
基於Cookie的認證方案可在Options項中可指定登入地址,
基於基本身份的認證方案可在Options項中指定使用者名稱/密碼;
2. 身份認證程式繼承自`AuthenticationHandler類`或`IAuthenticationHandler介面`。
![](https://imgkr2.cn-bj.ufileos.com/12b7d893-1c68-4626-ad62-f918576bcb7f.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=hf5u%252Bm9cWNkNCWJVWTgr5vaDp%252FQ%253D&Expires=1601822570)
- 核心認證函式可落地基於宣告的訪問控制,生成綁定了ClaimsPrincipal、Scheme的`AuthenticationTicket物件`; 無論認證成功/失敗,函式返回`AuthenticateResult物件`。
- 挑戰(對未認證的使用者做出的反應): 例如返回登入頁面
- 禁止(對已認證,但對特定資源無權訪問做出的反應) : 例如返回提示字串
以上均為服務註冊過程
3. 收到請求,認證中介軟體使用`IAuthenticationService`對HttpContext按照要求的scheme進行認證, 實際內部會呼叫第2步編寫的認證處理程式。
![](https://imgkr2.cn-bj.ufileos.com/333638e3-d5a6-4651-a43d-18ae2315f17a.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=xRvEYpXEQMYrE7tw0NwWE%252BIgq9s%253D&Expires=1601822194)
以上認證原理,之前有一個近身實戰: ASP.NET Core 實現基本身份驗證。
原始碼如下: https://www.cnblogs.com/JulianHuang/p/10345365.html
### 3. ASP.NET Core獲取當前使用者
> 基於宣告的訪問控制, 我們會在HttpContext.User屬性儲存身份資訊。
```
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier,username),
new Claim(ClaimTypes.Name,username),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
Context.User = principal;
```
Web應用程式中獲取當前登入使用者, 有兩種程式碼場合:
##### 3.1 在控制器中獲取當前登入使用者
控制器是處理請求的 一等公民,天生自帶HttpContext。
直接通過ControllerBase基類中包含的HttpContext屬性,獲取User物件。
> 實際上Razor Page、Razor View、Middleware均包含HttpContext屬性/引數, 可直接使用。
##### 3.2 在服務中獲取當前登入使用者
這個時候,服務是作為請求處理中的一個環節,並沒有直接可用的HttpContext。
ASP.NET Core 提供了`IHttpContextAccessor類`能夠注入此次請求中的HttpContext物件(依賴注入框架的作用)。
```
// 下面的使用者實體類,需要獲取當前登入使用者,藉助IHttpContextAccessor注入httpContext
public class UserEntityService : IUserEntityService
{
private IHttpContextAccessor _accessor;
private readonly IMongoCollection _users;
public UserEntityService(IHttpContextAccessor accessor, IDefaultMongoDatabaseProvider databaseProvider)
{
_accessor = accessor;
_users = databaseProvider.GetCollection(CollectionNames.UserProfiles);
}
public Task GetCurrentUserAsync()
{
var rawUser = this._accessor.HttpContext.User();
if (rawUser == null)
{
return null;
}
var filter = Builders.Filter.Eq("UserId", rawUser.UserId);
return _users.Find(filter).FirstOrDefaultAsync();
}
}
```
#### + abp vnext
我們不需要區分以上程式碼場合,在Controller或者Application 服務中使用`ICurrentUser`介面拿到登入使用者。
### 旁白
個人認為,ASP.NET Core身份認證的原始碼, 基於現實認知提煉而來,讓我們驚歎於框架程式碼的的簡潔精煉、層次分明。
基於宣告的訪問控制已成標準,ASP.NET Core/abp vnext 均提供了完善的