亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

域對象安全

1. 前言

相比 Web 請求的安全及方法調用級別的安全,有些應用還會定義更加復雜的訪問權限。在這種情況下,權限策略需要同時包含:

  • who」通過認證(Authentication)完成;
  • where」在什么地方應用;
  • what」安全對象是什么。

也就是說,權限策略除了考慮調用的方法,還有考慮調用域對象的實例。

舉例說明。假設我們設計一個寵物診所的管理系統,該系統有兩個主要用戶組:工作人員和客戶。員工可以訪問所有動物數據,而客戶只能查看自己的數據。假設我們為該系統擴展了新的功能,即客戶可以授權其他用戶查看自己的數據,比如其他寵物醫院的關聯機構,寵物俱樂部等等。在 Spring Security 項目中,我們有幾種實現方法:

  • 在業務方法中實現安全策略。

    比如,我們可以在「客戶」的域對象實例中放置集合,通過權限的配置內容,判斷集合中哪個用戶擁有訪問權限。這種方式下,我們使用 SecurityContextHolder.getContext().getAuthentication() 方式獲取權限對象。

  • 自定義訪問決策實例 AccessDecisionVoter 并配和 GrantedAuthority 對象。

    擴展實現 AccessDecisionVoter ,通過 Authentication 對象中 GrantedAuthority[] 集合的內容實現安全策略。這種方式下,我們需要在權限對象 GrantedAuthority 體現出該主體是否有對其他「客戶」的訪問權限。

  • 自定義訪問決策實例 AccessDecisionVoter 直接通過「客戶」域對象實例判定「客戶」權限。

    這種情況下 AccessDecisionVoter 對象需要有檢索「客戶」的數據訪問接口。

上述的方法都是適用的。但是,第一種方式中,授權檢查的代碼將會和業務代碼緊密關聯,耦合度高,不便于單元測試。適用 GrantedAuthority 的方式的缺點是需要對每一個「客戶」實例進行權限判斷,當「客戶」數量很大時,這種做法執行效率會降低。第三種做法相當于直接從外部獲取「客戶」的全部信息,相對前兩種效果更好一些,即實現了代碼分離,又降低了內存和計算量的消耗但是「客戶」對象被暴露了多次,第一次在權限判定時,第二次在業務邏輯時,這樣同樣降低了效率。同時,這三種方式都需要我們從頭開始編碼,所以這些方式都不是最佳方式。

Spring Security 為我們提供了一種便捷的域對象安全管理策略,本節主要討論域對象的權限策略。

2. 域對象安全概述

Spring Security 的域對象安全實現是通過 「ACLs(access control list)服務」方式實現。使用 Spring Security ACLs 服務,需要導入 spring-security-acl-xxx.jar 依賴包。

Spring Security 域對象安全功能以 ACL 的概念為核心,系統中每一個域對象實例都擁有各自的 ACL 配置表,該 ACL 記錄著該域訪問者的黑白名單列表。Spring Security 的 ACL 有三個主要操作:

  • 查詢和修改所有域對象的 ACL 配置
  • 在方法調用前,確保其主體參數可以被進行權限判定;
  • 在方法調用后,確保其主體返回可以被進行權限判斷。

這種方法的優勢在于 ACL 的存儲和檢索的高效性。系統中域對象的每個實例都可能被多次訪問,ACL 提供了高性能的查詢能力、可插拔、最小化死鎖的數據庫修改操作、代碼獨立及完整的封裝。

圖片描述

2.1 ACL 的存儲

以數據庫方式為例,使用數據庫作為 ACL 存儲時,需要用到四個數據表:

  • ACL_SID

    系統中任何身份或者權限信息,都有一個 SID,即他的安全唯一標識。該表包含列「ID」,文本類型,用于存儲 SID 值;和一個標志列「Flag」,用來描述該 SID 是身份或是權限。因此,每一個身份或者權限都只有一條數據,用來獲取授權,SID 也被稱為「接收者(recipient)」

  • ACL_CLASS

    用于標識域對象類型。包含列 ID 和域對象的 Java 類名。每一個域對象類名只有一條 ACL 記錄。

  • ACL_OBJECT_IDENTITY

    保存著系統里的所有域對象實例,包含列「ID」、「ACL_CLASS.ID」、「ACL_SID.ID」。

  • ACL_ENTRY

    保存著獨立的許可記錄。包含外鍵「ACL_OBJECT_IDENTITY.ID」,標識列表示是否允許或者拒絕,標識的格式是二進制的位掩碼形式。

ACL_ENTRY 中的掩碼位標志著是否允許被訪問。默認情況下0位代表讀、1位代表寫、2位代表創建、3位代表刪除、4位代表執行。

2.2 ACL 主要對象和接口

  • ACL。每個域對象都有且僅有一個「ACL」對象,該對象保持了 AccessControlEntry 及「ACL」的所有者?!窤CL」不直接引用域對象,而是引用 ObjectIdentity,存儲在 ACL_OBJECT_IDENTITY 表中。
  • AccessControlEntry?!窤CL」中包含多個 AccessControlEntry 對象,在框架中被簡寫成 ace。每個 ace 關聯 PermissionSid、ACL 的實例。ace 可以標記為許可,也可以標記為不允許,被存儲在 ACL_ENTRY 表中。
  • Permission。權限表示一個特定的不可變的位掩碼,具有匹配權限和信息輸出的功能。基本權限策略(0 位~4 位)包含在 BasePermission 類中。
  • Sid?!窤CL」模塊需要用到用戶的身份信息和權限信息。這些信息通過 Sid (Security identity)定位。常見的身份信息 Sid 類如 PrincipalSidGrantedAuthoritySid。這些信息存儲在 ACL_SID 表中。
  • ObjectIdentity。每個域對象在「ACL」模塊內部用 ObjectIdentity 表示。默認實現類為 ObjectIdentityImpl。
  • AclService。檢索適用于給定 ObjectIdentityAcl 實例。其實現類有 JDBCAclService 等,檢索操作委托給 LookupStrategy 完成。LookupStrategy 為檢索「ACL」信息提供了一種高度優化的策略,使用批處理檢索的方式「BasicLookupStrategy」,并支持利用視圖、分級查詢及其他高性能方案的「non-ANSI SQL」方式實現。
  • MutableAclService。允許「ACL」被修改變動。該接口如果不是必須的。

注意:現有的 AclService 及其數據庫相關類,使用的都是 ANSI-SQL。

3. 代碼演示

Spring Security 官方提供了兩個實例,它們演示了ACL模塊。第一個是關于聯系人的演示,第二個是文檔管理系統(DMS)案例。

使用 Spring Security ACL 功能的第一步,是確定 ACL 數據的存儲位置。這里需要實例化 DataSource,并將其注入到 JdbcMutableAclServiceBasicLookupStrategy 實例中。前者提供了修改的接口,后者用于提高「ACL」檢索效能。

當上述內容完成實例化之后,接下來我們需要確保域模型和 Spring Security ACL 的連通性。多數情況下域對象都包含 public Serializable getId() 方法,用來返回域對象的唯一標識。

關于如何創建「ACL」或者修改現有「ACL」請看以下代碼:

// 為 ACE 準備基本數據
ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
Sid sid = new PrincipalSid("Samantha");
Permission p = BasePermission.ADMINISTRATION;

// 創建 ACL 對象
MutableAcl acl = null;
try {
acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
acl = aclService.createAcl(oi);
}

// 通過 ACE 授予更多權限
acl.insertAce(acl.getEntries().length, p, sid, true);
aclService.updateAcl(acl);

該實例中,演示了如何檢索標識符為 44 的類型為 Foo 的域對象。而后我們創造了「ACE」,是名為「Samantha」的主體可以訪問和管理該對象。實例中 insertAce 方法的作用是插入條目,其最后一個 bool 值即為「允許」或「拒絕」,通常情況下,我們使用白名單「ACL」方式。

完成了上述內容后,我們需要在數據庫中維護好「ACL」信息,并將「ACL」信息作為授權決策邏輯的一部分來使用。

一旦您使用了上述技術在數據庫中存儲一些ACL信息,下一步就是實際使用ACL信息作為授權決策邏輯的一部分。這一步實現方式有很多,比如擴展 AccessDecisionInvestor 或者 AfterInvocationProvider,可以分別在方法執行前后觸發鑒權。這些方法使用 AclService 檢索「ACL」,然后調用 Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode) 決定是允許還是拒絕。同樣也可以使用 AclEntryVoter,AclEntryAfterInvocationProviderAclEntryAfterInvocationCollectionFilteringProvider 類,所有這些類都提供了基于聲明的方式去獲取 ACL 信息,所以不需要我們每次修改權限代碼。

4. 小結

本節討論了域對象的安全配置策略,主要內容有:

  • Spring Security 通過 ACL 方式實現高性能域對象的權限控制;
  • Spring Security ACL 鑒權有基于關系型數據庫的成熟解決方案;
  • Spring Security ACL 模塊降低了執行效率,也降低了開發工作量。

至此,關于權限部分的討論告一段落,從下節開始,我們討論 Spring Security 除了「認證」和「鑒權」之外的常用操作。