ASP.NET Core 基於宣告的訪問控制到底是什麼鬼?
阿新 • • 發佈:2020-09-25
從ASP.NET 4.x到ASP.NET Core,內建身份驗證已從基於角色的訪問控制(RBAC)轉變為`基於宣告的訪問控制(CBAC)`。
> 我們常用的HttpContext.User屬性ASP.NET 4.0時代是IPrincipal型別,ASP.NETCore現在強化為ClaimsPrincipal型別。
![](https://img2020.cnblogs.com/blog/587720/202009/587720-20200925100441784-1451471742.jpg)
本文就一起來看看這難纏的、晦澀難懂的宣告式訪問控制。
## 1.Claims : 宣告
宣告是基於宣告的身份驗證(claims-based authentication)的基礎,宣告是某`主題(Subject)`的片段資訊
> 宣告是以個名詞,並不能說明主體可以做什麼或不能做什麼, 對應現實生活中各種卡片上體現的片段資訊。
使用術語“**主題**”是因為宣告不僅限於描述使用者,宣告可能與應用程式,服務或裝置有關。
| 主題 | Claim1 | Claim2 | Claim3 | Claim3 | Claim5| Claim6 | Claim7 |Claim8 |
| --- | --- | --- | --- | --- | --- | --- | --- |--- |
| 身份證| 身份證號| 姓名| 性別| 籍貫 | 生日| 簽發機關| 簽發時間| 過期時間|
|工作狗牌 |姓名 |級別|花名| 身份證號| 性別| base地區| 入職時間|--- |
| 王者榮耀| 賬號 |遊戲等級| 大區| 角色 |氪金級別|年齡| 註冊時間| --- |--- |
| 微信 | 微訊號 |暱稱| 註冊時間| 國籍| 實名證件| 手機號 |---| --- |--- |
|車牌 | 車牌編號| 車牌所屬人| 車牌地區| 車牌性質| 簽發時間| 簽發機關|--- |--- |
| 某大保健會員卡 |卡號| 姓名| 手機號| 會員級別| 辦卡時間| 辦卡門店 |--- |--- |
```
// 宣告通過`System.Security.Claim`類表示。
public class Claim {
public string Type { get; }
public string Value { get; }
public string ValueType { get; }
// some properties have been omitted.
}
```
對比可見:每個宣告都有一個標識片段資訊型別的Type屬性、儲存片段資訊的Value屬性、片段資訊的資料型別。
```
var idClaim = new Claim(“Id”,“ 1”,“Integer”); // 使用者ID:整形
var dobClaim = new Claim(“dob”,“04/20/2000”,“Date”); // 生日:事件型別
var emailClaim = new Claim(nameof(ClaimTypes.Name), mockUser.Email,nameof(ClaimValueTypes.String)),
```
## 2. Identities: 身份
同一主題的宣告組合在一起,稱為ClaimsIdentity。
> 對應現實生活中**各種卡片**:身份證、工作狗牌、車牌、大保健會員卡,均體現了某一個主題。
```
public class ClaimsIdentity {
public string Name { get; }
public IEnumerable Claims { get; }
public string AuthenticationType { get; } // 儲存使用的身份驗證方法(Bearer、Basic)
public bool IsAuthenticated { get; }
// some properties have been omitted.
}
```
![](https://img2020.cnblogs.com/blog/587720/202009/587720-20200925100518457-565096826.png)
某WebAPI,該API可通過其唯一ID和名稱來識別使用者。驗證從使用者收到的承載令牌(JWT等)後,我們可以建立`ClaimsIdentity`來表示它們:
```
ClaimsIdentity userIdentity = new ClaimsIdentity(
new Claim[] {
new Claim("Id", "1"),
new Claim("Username", "Bert")
},
"Bearer"
);
//userIdentity.IsAuthenticated == true since we passed "Bearer" as AuthenticationType.
```
## 3. Principals: 主體
`ClaimsIdentity`可以方便地表示一個主題(一組宣告),很多時候一個主體有多個身份,就像現實生活中我們有個身份卡片,這個時候我們就需要**錢包或者賬號管理工具**(1Passwowd、LassPass)
接上面的例子, 如果WebAPI需要確保訪客使用的裝置處於白名單,則可以對訪客維護`裝置身份`:
```
ClaimsIdentity deviceIdentity = new ClaimsIdentity(
new Claim[] {
new Claim("IP", "192.168.1.1"),
new Claim("Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0")
}
);
// 針對訪客裝置宣告,不要設定AuthenticationType
```
將`使用者身份`和`裝置身份`兩個獨立的身份集中在一起就是`主體ClaimsPrincipal`
```
public class ClaimsPrincipal {
public IEnumerable Claims { get; }
public IEnumerable { get; }
public ClaimsIdentity Identity { get; }
public virtual IEnumerable FindAll(Predicate match);
public virtual bool HasClaim(string type, string value);
// ClaimsPrincipal提供了一些輔助方法/屬性來檢查事物,例如在任何關聯的身份中是否存在宣告.
}
```
主體物件代表程式碼執行的使用者的安全上下文,是各種有效身份的組合。
```
var principal = new ClaimsPrincipal(new IIdentity[] { userIdentity, deviceIdentity });
```
## 總結
基於宣告的訪問控制,本質是將散落的各個主題身份收集起來,自行表徵。
- Claims: 身份資訊的片段資料
- Identities: 各種身份資訊
- Principals: 主體,各種身份賬戶的集中儲存地
![](https://img2020.cnblogs.com/blog/587720/202009/587720-20200925100559798-1863669041.png)
- https://github.com/dotnet/runtime/blob/master/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs
- https://eddieabbondanz.io/post/aspnet/claims-based-authentication-claims-identities-pri