在使用Spring Security的Web應(yīng)用程序中浊伙,總會用到一些關(guān)鍵的過濾器摊阀,所以我們首先來看看這些過濾器及其支持類和接口蔬崩。我們不會涵蓋每一個特性涮瞻,因此如果您想獲得完整的圖片绣版,一定要查看JavaDoc中的特性胶台。
10.2.1?FilterSecurityInterceptor
在討論?access-control in general時,我們已經(jīng)簡要地看過了 FilterSecurityInterceptor?杂抽,并且我們已經(jīng)將它與命名空間的<intercept url>元素組合在一起以進(jìn)行內(nèi)部配置一起使用≌┗#現(xiàn)在,我們將看到如何用 FilterChainProxy 來顯式配置它缩麸,以及它的伴生過濾器 ExceptionTranslationFilter铸磅。典型配置示例如下所示:
<bean id="filterSecurityInterceptor"
? ? class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource">
? ? <security:filter-security-metadata-source>
? ? <security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>
? ? <security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
? ? </security:filter-security-metadata-source>
</property>
</bean>
FilterSecurityInterceptor?負(fù)責(zé)處理HTTP資源的安全性。它需要對 AuthenticationManager?和 AccessDecisionManager的引用。它還提供了適用于不同HTTP URL請求的配置屬性阅仔。請參閱技術(shù)介紹?the original discussion on these?吹散。
FilterSecurityInterceptor?可以通過兩種方式攜帶屬性參數(shù)進(jìn)行配置。第一個是使用<filter-security-metadata-source>namespace元素八酒,如上圖所示空民。這類似于命名空間章節(jié)中的<http>元素,但<intercept url>子元素僅使用pattern?和 access?屬性羞迷。逗號用于分隔應(yīng)用于每個HTTP URL的不同配置屬性界轩。第二種選擇是編寫自己的SecurityMetadataSource,但這超出了本文檔的范圍衔瓮。無論使用何種方法耸棒,SecurityMetadataSource?都負(fù)責(zé)返回一個包含與單個安全HTTP URL關(guān)聯(lián)的所有配置屬性的 List<ConfigAttribute>。
應(yīng)該注意报辱,FilterSecurityInterceptor.setSecurityMetadataSource()?方法實際需要FilterInvocationSecurityMetadataSource?的實例。這是SecurityMetadataSource類的一個子類单山。它只是表示SecurityMetadataSource?理解 FilterInvocations 碍现。為了簡單起見,我們將繼續(xù)將FilterInvocationSecurityMetadataSource?稱為SecurityMetadataSource米奸,因為這種區(qū)別與大多數(shù)用戶幾乎沒有關(guān)聯(lián)昼接。
由命名空間語法創(chuàng)建的 SecurityMetadataSource?通過將請求URL與已配置的 pattern?屬性進(jìn)行匹配來獲取特定?FilterInvocation?的配置屬性。這與命名空間配置的行為方式相同悴晰。默認(rèn)情況下慢睡,將所有表達(dá)式視為Apache Ant路徑,并用正則表達(dá)式來支持更復(fù)雜的情況铡溪。request-matcher?屬性用于指定正在使用的模式類型漂辐。不能在同一定義中混合表達(dá)式語法。例如棕硫,以前使用正則表達(dá)式而不是Ant路徑的配置將編寫如下:
<bean id="filterInvocationInterceptor"
? ? class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="runAsManager" ref="runAsManager"/>
<property name="securityMetadataSource">
? ? <security:filter-security-metadata-source request-matcher="regex">
? ? <security:intercept-url pattern="\A/secure/super/.*\Z" access="ROLE_WE_DONT_HAVE"/>
? ? <security:intercept-url pattern="\A/secure/.*\" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
? ? </security:filter-security-metadata-source>
</property>
</bean>
Patterns?總是按照其定義的順序進(jìn)行評估髓涯。因此,在列表中定義更具體的模式比定義不那么具體的模式要更靠前哈扮。這反映在上面的示例中纬纪,其中更具體的 /secure/super/ pattern比不那么具體的 /secure/ pattern?靠前。如果它們被顛倒滑肉,/secure/ pattern將始終匹配包各,而 /secure/super/ pattern將永遠(yuǎn)不會被評估。
10.2.2 異常處理過濾器 ExceptionTranslationFilter
ExceptionTranslationFilter??位于安全篩選器堆棧中 FilterSecurityInterceptor?的上方靶庙。它本身不執(zhí)行任何實際的安全強(qiáng)制问畅,但處理安全攔截器拋出的異常,并提供適當(dāng)?shù)腍TTP響應(yīng)。
<bean id="exceptionTranslationFilter"
????????class="org.springframework.security.web.access.ExceptionTranslationFilter">
????????<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
????????<property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</bean>
<bean id="authenticationEntryPoint"
????????class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
????????<property name="loginFormUrl" value="/login.jsp"/>
</bean>
<bean id="accessDeniedHandler"
? ? ? ?class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
????????<property name="errorPage" value="/accessDenied.htm"/>
</bean>
身份驗證入口點?AuthenticationEntryPoint
如果用戶請求受保護(hù)的HTTP資源按声,但未對其進(jìn)行身份驗證膳犹,則將調(diào)用 AuthenticationEntryPoint?。安全攔截器將沿著調(diào)用堆棧進(jìn)一步拋出適當(dāng)?shù)?AuthenticationException?或 AccessDeniedException?签则,從而在入口點觸發(fā) commence?方法须床。這樣做的目的是向用戶提供適當(dāng)?shù)捻憫?yīng),以便可以開始身份驗證渐裂。我們在這里使用的是LoginLauthenAuthenticationEntryPoint豺旬,它將請求重定向到不同的URL(通常是登錄頁面)。實際使用的AuthenticationEntryPoint?實現(xiàn)類取決于您希望在應(yīng)用程序中使用的身份驗證機(jī)制柒凉。
訪問拒絕控制器 AccessDeniedHandler
如果用戶已經(jīng)過身份驗證通過族阅,并且試圖訪問受保護(hù)的資源,會發(fā)生什么情況呢膝捞?在正常使用中坦刀,不應(yīng)該發(fā)生這種情況,因為應(yīng)用程序工作流應(yīng)限制為用戶可以訪問的操作蔬咬。例如鲤遥,指向管理頁面的HTML鏈接可能對沒有管理角色的用戶隱藏。但是林艘,您不能依靠隱藏鏈接來實現(xiàn)安全性盖奈,因為用戶總是有可能直接輸入URL來繞過這些限制『或者他們可以修改一個RESTful URL來更改一些參數(shù)值钢坦。您的應(yīng)用程序必須針對這些場景進(jìn)行保護(hù),否則它肯定是不安全的啥酱。通常爹凹,您將使用簡單的Web層安全性來對基本URL應(yīng)用約束,并在服務(wù)層接口上使用更具體的基于方法的安全性來真正確定允許的內(nèi)容镶殷。
如果拋出了 AccessDeniedException?逛万,并且用戶已經(jīng)通過了身份驗證,那么這意味著這個用戶嘗試了一個沒有足夠權(quán)限的操作批钠。在這種情況下宇植,ExceptionTranslationFilter?將調(diào)用第二個策略,即?AccessDeniedHandler埋心。默認(rèn)情況下指郁,使用 AccessDeniedHandlerImpl?,它只向客戶機(jī)發(fā)送403(禁止)響應(yīng)拷呆∠锌玻或者疫粥,您可以顯式配置一個實例(如上面的示例所示),并設(shè)置一個可以被請求轉(zhuǎn)發(fā)到的錯誤頁URL腰懂。這可以是一個簡單的“拒絕訪問”頁面梗逮,如JSP,也可以是一個更復(fù)雜的處理程序绣溜,如MVC控制器慷彤。當(dāng)然,您可以自己實現(xiàn)接口并使用自己的實現(xiàn)怖喻。
還可以在使用命名空間配置應(yīng)用程序時提供自定義的 AccessDeniedHandler?底哗。有關(guān)詳細(xì)信息,請參閱?the namespace appendix锚沸。
SavedRequests 和 RequestCache接口
ExceptionTranslationFilter?的另一個職責(zé)是在調(diào)用 AuthenticationEntryPoint 之前保存當(dāng)前請求跋选。這允許在用戶進(jìn)行身份驗證后還原請求(請參閱先前的Web身份驗證概述)。一個典型的例子是哗蜈,用戶使用一個表單登錄前标,然后由默認(rèn)的SavedRequestAwareAuthenticationSuccessHandler?(見下文)重定向到原始URL。
RequestCache?封裝了存儲和檢索 HttpServletRequest? 實例所需的功能距潘。默認(rèn)情況下炼列,使用HttpSessionRequestCache?,它將請求存儲在 HttpSession?中绽昼。當(dāng)用戶被重定向到原始URL時,RequestCacheFilter的任務(wù)是從緩存中實際還原保存的請求须蜗。
在正常情況下硅确,您不需要修改任何此功能,但保存的請求處理是一種“盡最大努力”的方法明肮,并且可能存在默認(rèn)配置無法處理的情況菱农。這些接口的使用使得它可以從SpringSecurity3.0開始完全插入。
10.2.3 安全上下文持久性篩選器?SecurityContextPersistenceFilter
我們在技術(shù)概述一章中介紹了這個非常重要的過濾器的用途柿估,因此您可能希望在此時重新閱讀該部分循未。讓我們先看看如何配置它與 FilterChainProxy 一起使用★啵基本配置只需要bean本身的妖。
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
正如我們之前看到的,這個過濾器有兩個主要任務(wù)足陨。它負(fù)責(zé)在HTTP請求之間存儲SecurityContext內(nèi)容嫂粟,并在請求完成時清除 SecurityContextHolder。清除存儲上下文的 ThreadLocal?是非常重要的墨缘,因為線程可能會被替換到servlet容器的線程池中星虹,而特定用戶的安全上下文仍然附加零抬。該線程隨后可能會在后期使用,使用錯誤的憑據(jù)執(zhí)行操作宽涌。
安全上下文存儲庫?SecurityContextRepository
從SpringSecurity3.0開始平夜,加載和存儲安全上下文的工作現(xiàn)在被委托給一個單獨的策略接口:
public interface SecurityContextRepository {
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
void saveContext(SecurityContext context, HttpServletRequest request,
? ? ? ? HttpServletResponse response);
}
HttpRequestResponseHolder?只是接收請求和響應(yīng)對象的容器,允許用包裝類的實現(xiàn)來替換這些對象卸亮。返回的內(nèi)容將傳遞到過濾器鏈忽妒。
默認(rèn)實現(xiàn)是 HttpSessionSecurityContextRepository,它將安全上下文存儲為 HttpSession?屬性嫡良。此實現(xiàn)最重要的配置參數(shù)是 allowSessionCreation?屬性锰扶,該屬性默認(rèn)為 true,從而允許類在需要會話時為經(jīng)過身份驗證的用戶存儲安全上下文時創(chuàng)建 session(除非進(jìn)行了身份驗證寝受,并且安全上下文的內(nèi)容發(fā)生了改變坷牛,否則不會創(chuàng)建)。如果不希望創(chuàng)建session?很澄,則可以將此屬性設(shè)置為 false:
<bean id="securityContextPersistenceFilter"?
? ? class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name='securityContextRepository'>
? ? <bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
? ? <property name='allowSessionCreation' value='false' />
? ? </bean>
</property>
</bean>
或者京闰,你可以提供一個 NullSecurityContextRepository 實例,一個空對象實現(xiàn)甩苛,它將阻止存儲安全上下文蹂楣,即使在請求期間已經(jīng)創(chuàng)建了會話。
10.2.4?UsernamePasswordAuthenticationFilter
我們現(xiàn)在看到了三個主要的過濾器讯蒲,它們總是出現(xiàn)在Spring Security Web配置中痊土。這三個元素也是由namespace <http>?元素自動創(chuàng)建的,不能用替代項替換∧郑現(xiàn)在唯一缺少的是一個實際的身份驗證機(jī)制赁酝,它允許用戶進(jìn)行身份驗證。此篩選器是最常用的身份驗證篩選器旭等,也是最常用的自定義篩選器酌呆。它還提供由命名空間中的<form login>元素使用的實現(xiàn)。配置它需要三個階段搔耕。
?像上面所做的那樣隙袁,使用登錄頁面的URL配置一個LoginLauthenAuthenticationEntryPoint,并將其設(shè)置在ExceptionTranslationFilter上弃榨。
?實現(xiàn)登錄頁面(使用JSP或MVC控制器)菩收。
?在應(yīng)用程序上下文中配置 UsernamePasswordAuthenticationFilter? 的實例
?將過濾器bean添加到您的過濾器鏈代理(確保您關(guān)注順序)。
登錄表單只包含用戶名和密碼輸入字段鲸睛,并發(fā)布到由過濾器監(jiān)控的URL(默認(rèn)為/login)坛梁。基本的過濾器配置如下所示:
<bean id="authenticationFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
Application Flow on Authentication Success and Failure
過濾器調(diào)用配置的 AuthenticationManager?來處理每個身份驗證請求腊凶。成功身份驗證或身份驗證失敗后的目標(biāo)分別由AuthenticationSuccessHandler?和 AuthenticationFailureHandler?策略接口控制划咐。過濾器具有允許您設(shè)置屬性拴念,因此您可以完全自定義行為。提供了一些標(biāo)準(zhǔn)實現(xiàn)褐缠,如 SimpleUrlAuthenticationSuccessHandler政鼠、SavedRequestAwareAuthenticationSuccessHandler、SimpleUrlAuthenticationFailureHandler队魏、ExceptionMappingAuthenticationFailureHandler?和 DelegatingAuthenticationFailureHandler公般。多了解這些類的javadoc,以及?AbstractAuthenticationProcessingFilter?胡桨,了解它們的工作原理和支持的功能官帘。
如果身份驗證成功,則生成的?Authentication?將放入SecurityContextHolder昧谊。然后刽虹,將調(diào)用配置的AuthenticationSuccessHandler?,將用戶重定向或轉(zhuǎn)發(fā)到適當(dāng)?shù)哪繕?biāo)呢诬。默認(rèn)情況下涌哲,會使用SavedRequestAwareAuthenticationSuccessHandler?,這意味著用戶將被重定向到請求登錄之前所請求的原始目標(biāo)尚镰。
ExceptionTranslationFilter?緩存用戶發(fā)出的原始請求阀圾。當(dāng)用戶進(jìn)行身份驗證時,請求處理程序使用此緩存請求獲取原始URL并重定向到該URL狗唉。然后重新構(gòu)建原始請求并將其用作替代請求初烘。
如果身份驗證失敗,將調(diào)用配置的 AuthenticationFailureHandler?分俯。