ASP.NET Core 基於聲明的訪問控制到底是什麼鬼?

  • 2020 年 9 月 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 });

總結

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