Spring Security 自定義 (第二部分 - 即時調整安全會話)

工程 | Oleg Zhurakousky | 2009年1月3日 | ...

想象一下,你正處於一個安全會話中(你已登入並被授權訪問特定資源),但你的安全基礎設施團隊更新了你的許可權和特權。也許你被授予了更多許可權和特權,也許你的許可權被完全撤銷了... 問題在於,你的安全會話在會話登錄檔中已經註冊,在你登出/重新登入之前,代表你在該安全會話中的主體 (Principal) 將不會被重新建立。更戲劇性的是什麼情況(畢竟我們這裡討論的是安全問題)... 你是一名心懷不滿的員工,你的直屬管理層發現了你的“不當行為”,但你的公司需要開5個會議和提交10份審批表才能處理此事,在此期間你是否可以自由地造成更多損害???

顯然,有許多業務場景可能需要在使用者處於會話期間調整使用者的許可權。在 Spring Security 中,顯然也有不止一種方法可以實現這一點。一種方法是利用會話登錄檔(它實際上是用於併發會話控制的)將使用者踢出系統。透過構建一個 GUI 或啟用 JMX,可以從已註冊會話列表中“使”某個使用者“過期”。他們發起的下一個請求將被拒絕,會話將失效,從而強制進行身份驗證過程。然而,為了更有趣地演示實現類似目標的另一種方法,讓我們稍微改變一下場景,假設我們不想完全使使用者的安全會話失效,而只是想暫時掛起它。

現在,掛起 (suspend) 這個詞在安全的語境中可能意味著很多不同的事情。它可以表示臨時掛起,也可以表示完全撤銷權利。我將把這個詞在任何上下文中的具體含義留給你自己定義... 我更願意做的是向你展示如何使用 Spring Security 元件,如 AccessDecisionVoterAccessDecisionManager,來即時調整安全會話。在下面的例子中,我們還將透過暫時掛起使用者的權利而不使其會話失效的方式來調整當前的安全會話。

為了實現這一點,我們需要遵循以下步驟。

1. 在 Spring Security 配置中定義 AccessDecisionManager     1.1 定義基本的投票器,例如 RoleVoter 和 AuthenticatedVoter 2. 定義並實現 AccessDecisionVoter。這個投票器必須維護一個被撤銷權利的使用者列表,並且在使用者執行安全操作時必須投 ACCESS_DENIED 票。 3. 將這個投票器新增到 AccessDecisionManager 已註冊的投票器堆疊中。

此外,為了能夠在應用程式執行時與該投票器進行互動,我們還將使用 Spring JMX 將該投票器動態匯出為 JMX Bean,從而啟用其 JMX 功能。

基本就是這樣。

因此,首先我們需要定義 AccessDecisionManager (ADM)。由於我們使用了 Spring Security 名稱空間(只要可能),像 "security:http" 這樣的配置元素,Spring Security 會為我們註冊預設的 AccessDecisionManager,它碰巧是 AffirmativeBased ADM。這對我們來說不太好,因為我們在這裡希望更保守一些。所以,我們要做的就是定義一個 UnanimousBased ADM,並使用指向 UnanimousBased ADM 的 access-decision-manager-ref 屬性覆蓋 "security:http" 元素的預設 ADM(見下文)。

<!-- 定義自定義投票器 --> <bean id="suspendVoter" class="org.springframework.security.sample.SuspendRealTimeVoter"/> <!-- 將 AccessDecisionManager 定義為 UnanimousBased --> <bean id="accessDecisionManager" class="org.springframework.security.vote.UnanimousBased">   <property name="decisionVoters">     <list>       <ref bean="suspendVoter" />       <bean class="org.springframework.security.vote.RoleVoter" />       <bean class="org.springframework.security.vote.AuthenticatedVoter" />     </list>   </property> </bean>

我們還將把自定義投票器放在投票器堆疊中的首位,因為我們知道在 UnanimousBased(一致透過)的決策過程中,第一個 NO 意味著不應進行進一步評估。意識到其他投票決策可能耗時或影響效能,將自定義投票器首先註冊到投票器堆疊中可以確保只有在許可權未被掛起時才執行其他投票操作。

<!-- 定義 http 安全配置 --> <security:http access-decision-manager-ref="accessDecisionManager" . . . . .> . . . . </security:http>

剩下的唯一事情是 JMX 啟用 "suspendVoter"。這使用 Spring JMX 很容易完成。 <!-- JMX 啟用自定義投票器 --> <bean class="org.springframework.jmx.export.MBeanExporter">   <property name="beans">     <map>       <entry key="org.springframework.security:name=SuspendRealTimeVoter" value-ref="suspendVoter" />     </map>   </property> </bean>

現在,"suspendVoter" 將被匯出到 JMX 伺服器中,名稱為 org.springframework.security:name=SuspendRealTimeVoter

從程式碼的角度來看,我們唯一需要實現的自定義元件是 SuspendRealTimeVoter 類。在該類中,我們將維護一個使用者 Set,這些使用者的權利已被臨時撤銷。在 vote(..) 方法內部,我們的邏輯相當簡單。如果使用者在列表中,則投 ACCESS_DENIED 票,否則投 ACCESS_GRANTED 票。

public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {     String userName = authentication.getName();     return revokedUsers.contains(userName) ? ACCESS_DENIED : ACCESS_GRANTED; }

在那裡,你還會看到 suspend(String userName)grant(String userName) 方法,我們將透過它們來測試此功能。

注意:要透過 JMX 與我們的投票器進行互動,我們將使用你的 JDK (1.5+) 提供的 JConsole 工具,因此需要為我們的應用伺服器啟用 JMX。在這個例子中,我使用的是 Tomcat,所以如果你也使用 Tomcat,請在其啟動指令碼中新增以下 VM 引數

-Dcom.sun.management.jmxremote

啟動伺服器,部署應用程式並訪問其 URL: https://:8080/spring-security-sample-suspendUser

成功登入後,點選幾次重新整理,檢視你的會話是否仍然活躍並正常執行。然後導航到你的 JDK 的 bin 目錄,開啟命令列並輸入 jconsole(見下文)

JConsole 開啟後,點選 MBeans 選項卡。瀏覽樹並訪問 SuspendRealTimeVoter。

你會看到 Operations 選項卡下提供了 suspend(..)grant(..) 方法。掛起你當前登入使用者的權利,然後重新整理你所在的 HTML 頁面。你將看到 denied.html 頁面。透過呼叫該使用者的 grant(..) 方法取消掛起,你就可以恢復正常。現在,如果真的需要開5個會議和提交10份審批表,你可以臨時撤銷使用者的權利,給你的經理們足夠的時間來做出此類決定。

結論

這只是一個例子,演示瞭如何使用投票器臨時掛起安全會話。但我希望你能清楚地看到,同樣的方法也可以用於實現其他目標。這些目標可以是自動重新認證使用者,或者僅僅更新其 GrantedAuthorities 列表等等。

本文的示例原始碼可在此下載:spring-security-sample-suspenduser

獲取 Spring 新聞通訊

訂閱 Spring 新聞通訊,保持聯絡

訂閱

快人一步

VMware 提供培訓和認證,助你快速前進。

瞭解更多

獲取支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視全部