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型別。


本文就一起來看看這難纏的、晦澀難懂的宣告式訪問控制。

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 });

總結

基於宣告的訪問控制,本質是將散落的各個主題身份收集起來,自行表徵。

相關文章