從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.
}
每張身份卡片,有一個顯著的Name
, 通過源碼得知Name值
取自特定 _nameClaimType
:
public virtual string? Name
{
// just an accessor for getting the name claim
get
{
Claim? claim = FindFirst(_nameClaimType);
if (claim != null)
{
return claim.Value;
}
return null;
}
}
所以為確保 Identity有Name, 請傳入有效的NameType
某WebAPI,該API可通過其唯一ID和名稱來識別用戶。驗證從用戶收到的承載令牌(JWT等)后,我們可以創建ClaimsIdentity
來表示它們:
ClaimsIdentity userIdentity = new ClaimsIdentity(
new Claim[] {
new Claim("Id", "1"),
new Claim("Username", "Bert")
},
"Bearer",
"UserName",
"Role"
);
//userIdentity.IsAuthenticated == true since we passed "Bearer" as AuthenticationType.
// userIdentity.Name= "Bert" since FindFirst type="UserName"
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 });
總結
基於聲明的身份是應用程序用來獲取有關另一個應用程序已通過身份驗證的用戶的身份信息的常用方法。
聲明是關於用戶的簽發信息,這些信息已打包並簽名為安全令牌,並由發行者/身份認簽發機構通過安全令牌服務(STS)發送給依賴方應用程序
在基於聲明的身份驗證方法中,信任是顯式的。您的應用程序僅在信任簽發機構的情況下,才相信有關當前用戶的簽發信息
-
Claims: 身份信息的片段數據
-
Identities: 各種身份信息
-
Principals: 主體,各種身份賬戶的集中存儲地
-
https://eddieabbondanz.io/post/aspnet/claims-based-authentication-claims-identities-principals/