7.3 控制通路
身份驗證隻是Spring Security安全保護機制中的第一步。一旦Spring Security弄清使用者的身份後,它必須決定是否允許使用者通路由它保護的資源。在圖7.1中,我們已經配置了認證管理器。現在,該配置通路決策管理器 了。通路決策管理器負責決定使用者是否擁有恰當的權限通路受保護的資源。它們是由 org.acegisecurity.AccessDecisionManager接口定義的:
![]() |
這裡的supports()方法根據受保護資源的類類型以及它的配置屬性(受保護資源的通路要求)來 判斷通路決策管理器是否能夠針對該資源做出通路決策。這裡的decide()方法是完成最終決定的地方。如果它沒有抛出一個 AccessDeniedException或者是InsufficientAuthenticationException而傳回,則允許通路受保護的 資源。否則,通路被拒絕。
7.3.1 通路決策投票
Spring Security的通路決策管理器最終負責為一個已認證身份驗證的使用者确定通路權限。不過,它們并不是完全依靠自己而完成這些決策的,而是通過征詢一個或 多個對某一使用者是否有權通路受保護資源進行投票的對象。一旦獲得所有的投票結果,決策管理器便将統計得票情況,并做出它的最終決定。
如表7.3所列,Spring Security帶有AccessDecisionManager的三個實作,每一個都采用一種不同的方法來統計得票情況。
表7.3 Spring Security的通路決策管理器通過統計是否允許
某一使用者進入的得票來幫助決定是否同意該使用者通路
通路決策管理器 | 如何決定同意 / 拒絕通路 |
org.acegisecurity.vote.AffirmativeBased | 隻要有一個投票者投票贊成授予通路權,就允許通路 |
org.acegisecurity.vote.ConsensusBased | 隻要大多數投票者投票贊成授予通路權,就允許通路 |
org.acegisecurity.vote.UnanimousBased | 隻有當所有投票者都投票贊成授予通路權時,才允許通路 |
在Spring配置檔案中,所有的通路決策管理器都是以相同的方式進行配置的。比如說,下面這一段XML選段配置了一個UnanimousBased通路決策管理器:
|
通過這裡的decisionVoters屬性,你可以為通路決策管理器提供一組投票者。在上述情況中,隻有一個投票者,它引用了一個名為roleVoter的Bean。下面讓我們看一下這個roleVoter是如何配置的。
7.3.2 決定如何投票
盡管通路決策投票者對是否授權通路某個受保護的資源沒有最終發言權(該項工作屬于通路決策管理器),但是它們在通路決策過程中扮演着重要的角色。一 個通路決策投票者的工作是同時考慮使用者已擁有的授權和受保護資源配置屬性中所要求的授權。基于這些資訊,通路決策投票者通過投票為通路決策管理器做出決策 提供支援。
任何實作org.acegisecurity.vote.AccessDecisionVoter接口的對象都是一個通路決策投票者:
|
可以看到,這個AccessDecisionVoter接口和AccessDecisionManager接口非常相似。最大的差別在于,這裡沒有 傳回void的decide()方法,而是一個傳回int的vote()方法。那是因為通路決策投票者并不決定是否允許通路,它僅僅就是否授予通路權限投 出它自己的一票。
在獲得投票機會時,通路決策投票者能夠以下面三種方式之一進行投票:
ACCESS_GRANTED——投票者希望允許通路受保護的資源。
ACCESS_DENIED——投票者希望拒絕對受保護資源的通路。
ACCESS_ABSTAIN——投票者保持中立。
與絕大多數的Spring Security元件相同,你也可以自由地編寫自己的AccessDecision Voter的實作類。然而,Spring Security提供有一個很實用的投票者實作類RoleVoter,它在受保護資源的配置屬性代表一個角色時進行投票。說得更具體一些,就是在受保護資 源有一個名稱以ROLE_打頭的配置屬性時,RoleVoter參與投票。
RoleVoter決定其投票結果的方式是通過簡單地将受保護資源的所有配置屬性(以ROLE_作為字首)與已授予認證使用者的所有權限進行比較。如 果RoleVoter發現其中有一個是比對的,則它投ACCESS_GRANTED票。否則,它将投ACCESS_DENIED票。
RoleVoter将隻在通路所需的授權不是以ROLE_為字首時放棄投票。舉例來說,如果受保護的資源僅僅要求非角色的授權(比如說CREATE_USER),那麼RoleVoter将放棄投票。
你可以在Spring配置檔案中使用下列XML配置一個RoleVoter:
|
如前所述,RoleVoter隻有在受保護資源具有以ROLE_為字首的配置屬性時才進行投票。然而,這個ROLE_字首隻是預設值。你可以選擇通過設定rolePrefix屬性來覆寫這個預設字首:
|
在這裡,預設的字首已被覆寫為GROUP_。是以,這個RoleVoter現在将隻針對以GROUP_為字首的權限進行授權投票。
7.3.3 處理投票棄權
在知道了任何一個投票者都可以投允許、拒絕或棄權票之後,現在你可能會問:如果所有投票者都投棄權票,會發生什麼呢?那個使用者究竟會被授權還是拒絕通路?
在預設情況下,如果所有投票者全都投棄權票,那麼所有的通路決策管理者都将拒絕對資源的通路。不過,你可以通過将通路決策管理者的allowIfAllAbstain屬性設定為true來覆寫這個預設行為:
|
通過把allowIfAllAbstain設定為true,你就确立了一個“沉默即同意”的政策。換句話說,如果所有的投票者都放棄投票,則如同它們都投贊成票一樣,通路将被授權。
現在,你已經看到Spring Security的身份驗證和通路控制管理器是如何工作的了,下面讓我們把它們投入使用。在下一節中,你将看到如何使用Spring Security的一組Servlet過濾器來保護一個Web應用程式。稍後,在第7.6節,我們将深入到一個應用程式内部,考察如何使用Spring AOP在方法調用級上實施保護。