領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多這是我希望成為一系列關於 Spring Security 定製的小帖子系列的第一部分,展示了實際示例。這些定製的需求並非虛構,全部來自於實際應用中。。。
假設您有以下需求。您有一個角色列表,其中每個角色都包含適用於該角色的業務功能列表(見下文)
ROLE_ADMIN BF_QUOTE_CREATE BF_POLICY_CREATE BF_POLICY_DELETE
ROLE_AGENT BF_QUOTE_CREATE BF_POLICY_CREATE
ROLE_USER BF_QUOTE_CREATE
關鍵在於能夠基於兩者做出授權決策。
例如: 擁有 ROLE_ADMIN 角色的使用者應被授權訪問受此角色保護的任何資源。 <sec:authorize ifAllGranted="ROLE_ADMIN"> <p><a href="http://www.google.com">Google</a> </sec:authorize>
或 @Secured("ROLE_ADMIN") public void foo() . . . }
同一使用者應被授權訪問受相應業務功能保護的任何資源。 <sec:authorize ifAllGranted="BF_POLICY_DELETE"> <p><a href="http://www.google.com">Google</a> </sec:authorize>
或 @Secured("BF_POLICY_DELETE") public void foo() . . . }
實際上有幾種方法可以處理此需求。其中一種方法是建立一個 RoleHierarchy 並使用 RoleHierarchyVoter 來遍歷角色層次結構。這種方法的缺點是,在當前 Spring Security 2.0.4 的實現中,標籤庫 (security: authorize . . . ) 不會透過 AccessDecisionManager 來做出決策,因此在決定保護 HTML 元素時,Voters 不起任何作用。然而,考慮到 Spring Security 驚人的靈活性和定製能力,完成此需求仍然相當簡單。Spring Security 最大的優勢之一是可以圍繞如何建立 Principal (UserDetails 物件) 進行定製。建立 UserDetails 物件時,會填充 GrantedAuthorities 列表。之後會檢查此列表,以匹配保護資源的 GrantedAuthority。我們可以進行的一項定製是在建立 UserDetails 物件期間定製 GrantedAuthorities 列表。
在提供的示例中,有兩個屬性檔案(為簡化起見,我使用屬性檔案,但您可以輕鬆修改為使用 DB 或 LDAP)。一個檔案 users.properties 將使用者對映到 roles oleg=powder,ROLE_ADMIN
,而另一個檔案 role-to-bf.properties 將角色對映到 business functions 的列表 ROLE_ADMIN=BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE
。我們的目標是建立一個 UserDetails 物件,其中包含表示 roles 和 business functions 的 GrantedAuthorities 列表。例如:對於使用者 oleg,GrantedAuthorities 列表應為: ROLE_ADMIN,BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE
。因此,我們只需定義一個 UserDetailsService 的自定義實現,透過使用這兩個屬性檔案(在實際應用中可以是 DB 或 LDAP),我們將建立自定義的 GrantedAuthorities 列表,然後將它們注入到最終的 UserDetails 物件中。這相當簡單,我們可以重用現有的 GrantedAuthority 介面實現,例如 GrantedAuthorityImpl。然而,我們還想確保能夠追溯(用於除錯或任何其他目的)表示業務功能的每個 GrantedAuthority 的父級 GrantedAuthority。為了同時實現這兩個目標,我們將透過定義一個 BusinessFunctionGrantedAuthority 類來擴充套件 GrantedAuthorityImpl,該類簡單地包含定義此類 business function 的所有父級 GrantedAuthority 物件的列表。 public class BusinessFunctionGrantedAuthority extends GrantedAuthorityImpl { private List<GrantedAuthority> parentAuthorities; . . . }
然後,我們將建立一個 UserDetailsService 的自定義實現,並實現 loadUserByName(..) 方法,在該方法中我們將執行以下操作
1. 根據 users.properties 檔案的內容建立 UserAttribute 物件。UserAttribute 將包含表示角色的 GrantedAuthorities 列表。
2. 遍歷 role-GratedAuthorities 列表,併為每個 role-GrantedAuthority 建立一個 BusinessFunctionGrantedAuthority,並將其新增到已建立的 GrantedAuthorities 總列表。
2.1 將父 GrantedAuthority 新增到每個 BusinessFunctionGrantedAuthority。
3. 建立包含 GrantedAuthorities 完整列表的最終 UserDetails 物件。
然後在您的 Spring Security 配置中定義您的 AuthenticationProvider
注意:我們正在將 AuthenticationProvider 注入到由 ComplexAuthorityUserDetailsService 類實現的自定義 UserDetailsService 中(更多詳細資訊請參閱示例程式碼)。
保護您的資源,部署並訪問應用程式:https://:8080/spring-security-sample-grantedAuthority/index.jsp
登入後,您應該會看到顯示的 GrantedAuthorities 列表以及 Principal 的其他屬性。
您可以清楚地看到,表示 business function 的 GrantedAuthority 也顯示了定義此類 business function 的父級 GratedAuthorities 列表。檢查 index.jsp 並觀察 security:authorize 標籤如何同時使用 roles 和 business functions 來保護 HTML 元素。這就是全部了。您可以清楚地看到,透過少量定製,您可以輕鬆擴充套件和定製 Principal 的結構,並基於自定義 GrantedAuthorities 應用宣告式保護,而無需在業務程式碼中混入自定義安全程式碼。
示例程式碼可在此下載:spring-security-sample-grantedauthority