從ASP.NET 4.x到ASP.NET Core,內建身份驗證已從基於角色的訪問控制(RBAC)轉變為基於宣告的訪問控制(CBAC)
。
我們常用的HttpContext.User屬性ASP.NET 4.0時代是IPrincipal型別,ASP.NETCore現在強化為ClaimsPrincipal型別。
本文就一起來看看這難纏的、晦澀難懂的宣告式訪問控制。
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<Claim> Claims { get; }
public string AuthenticationType { get; } // 儲存使用的身份驗證方法(Bearer、Basic)
public bool IsAuthenticated { get; }
// some properties have been omitted.
}
某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<Claim> Claims { get; }
public IEnumerable<ClaimsIdentity> { get; }
public ClaimsIdentity Identity { get; }
public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match);
public virtual bool HasClaim(string type, string value);
// ClaimsPrincipal提供了一些輔助方法/屬性來檢查事物,例如在任何關聯的身份中是否存在宣告.
}
主體物件代表程式碼執行的使用者的安全上下文,是各種有效身份的組合。
var principal = new ClaimsPrincipal(new IIdentity[] { userIdentity, deviceIdentity });
總結
基於宣告的訪問控制,本質是將散落的各個主題身份收集起來,自行表徵。
-
Claims: 身份資訊的片段資料
-
Identities: 各種身份資訊
-
Principals: 主體,各種身份賬戶的集中儲存地
-
https://eddieabbondanz.io/post/aspnet/claims-based-authentication-claims-identities-principals/