在使用Spring Security的Web應(yīng)用程序中,總會(huì)用到一些關(guān)鍵的過(guò)濾器,所以我們首先來(lái)看看這些過(guò)濾器及其支持類(lèi)和接口。我們不會(huì)涵蓋每一個(gè)特性,因此如果您想獲得完整的圖片,一定要查看JavaDoc中的特性。
10.2.1?FilterSecurityInterceptor
在討論?access-control in general時(shí),我們已經(jīng)簡(jiǎn)要地看過(guò)了 FilterSecurityInterceptor?,并且我們已經(jīng)將它與命名空間的<intercept url>元素組合在一起以進(jìn)行內(nèi)部配置一起使用?,F(xiàn)在,我們將看到如何用 FilterChainProxy 來(lái)顯式配置它,以及它的伴生過(guò)濾器 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資源的安全性。它需要對(duì) AuthenticationManager?和 AccessDecisionManager的引用。它還提供了適用于不同HTTP URL請(qǐng)求的配置屬性。請(qǐng)參閱技術(shù)介紹?the original discussion on these?。
FilterSecurityInterceptor?可以通過(guò)兩種方式攜帶屬性參數(shù)進(jìn)行配置。第一個(gè)是使用<filter-security-metadata-source>namespace元素,如上圖所示。這類(lèi)似于命名空間章節(jié)中的<http>元素,但<intercept url>子元素僅使用pattern?和 access?屬性。逗號(hào)用于分隔應(yīng)用于每個(gè)HTTP URL的不同配置屬性。第二種選擇是編寫(xiě)自己的SecurityMetadataSource,但這超出了本文檔的范圍。無(wú)論使用何種方法,SecurityMetadataSource?都負(fù)責(zé)返回一個(gè)包含與單個(gè)安全HTTP URL關(guān)聯(lián)的所有配置屬性的 List<ConfigAttribute>。
應(yīng)該注意,FilterSecurityInterceptor.setSecurityMetadataSource()?方法實(shí)際需要FilterInvocationSecurityMetadataSource?的實(shí)例。這是SecurityMetadataSource類(lèi)的一個(gè)子類(lèi)。它只是表示SecurityMetadataSource?理解 FilterInvocations 。為了簡(jiǎn)單起見(jiàn),我們將繼續(xù)將FilterInvocationSecurityMetadataSource?稱(chēng)為SecurityMetadataSource,因?yàn)檫@種區(qū)別與大多數(shù)用戶幾乎沒(méi)有關(guān)聯(lián)。
由命名空間語(yǔ)法創(chuàng)建的 SecurityMetadataSource?通過(guò)將請(qǐng)求URL與已配置的 pattern?屬性進(jìn)行匹配來(lái)獲取特定?FilterInvocation?的配置屬性。這與命名空間配置的行為方式相同。默認(rèn)情況下,將所有表達(dá)式視為Apache Ant路徑,并用正則表達(dá)式來(lái)支持更復(fù)雜的情況。request-matcher?屬性用于指定正在使用的模式類(lèi)型。不能在同一定義中混合表達(dá)式語(yǔ)法。例如,以前使用正則表達(dá)式而不是Ant路徑的配置將編寫(xiě)如下:
<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)行評(píng)估。因此,在列表中定義更具體的模式比定義不那么具體的模式要更靠前。這反映在上面的示例中,其中更具體的 /secure/super/ pattern比不那么具體的 /secure/ pattern?靠前。如果它們被顛倒,/secure/ pattern將始終匹配,而 /secure/super/ pattern將永遠(yuǎn)不會(huì)被評(píng)估。
10.2.2 異常處理過(guò)濾器 ExceptionTranslationFilter
ExceptionTranslationFilter??位于安全篩選器堆棧中 FilterSecurityInterceptor?的上方。它本身不執(zhí)行任何實(shí)際的安全強(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>
身份驗(yàn)證入口點(diǎn)?AuthenticationEntryPoint
如果用戶請(qǐng)求受保護(hù)的HTTP資源,但未對(duì)其進(jìn)行身份驗(yàn)證,則將調(diào)用 AuthenticationEntryPoint?。安全攔截器將沿著調(diào)用堆棧進(jìn)一步拋出適當(dāng)?shù)?AuthenticationException?或 AccessDeniedException?,從而在入口點(diǎn)觸發(fā) commence?方法。這樣做的目的是向用戶提供適當(dāng)?shù)捻憫?yīng),以便可以開(kāi)始身份驗(yàn)證。我們?cè)谶@里使用的是LoginLauthenAuthenticationEntryPoint,它將請(qǐng)求重定向到不同的URL(通常是登錄頁(yè)面)。實(shí)際使用的AuthenticationEntryPoint?實(shí)現(xiàn)類(lèi)取決于您希望在應(yīng)用程序中使用的身份驗(yàn)證機(jī)制。
訪問(wèn)拒絕控制器 AccessDeniedHandler
如果用戶已經(jīng)過(guò)身份驗(yàn)證通過(guò),并且試圖訪問(wèn)受保護(hù)的資源,會(huì)發(fā)生什么情況呢?在正常使用中,不應(yīng)該發(fā)生這種情況,因?yàn)閼?yīng)用程序工作流應(yīng)限制為用戶可以訪問(wèn)的操作。例如,指向管理頁(yè)面的HTML鏈接可能對(duì)沒(méi)有管理角色的用戶隱藏。但是,您不能依靠隱藏鏈接來(lái)實(shí)現(xiàn)安全性,因?yàn)橛脩艨偸怯锌赡苤苯虞斎險(xiǎn)RL來(lái)繞過(guò)這些限制。或者他們可以修改一個(gè)RESTful URL來(lái)更改一些參數(shù)值。您的應(yīng)用程序必須針對(duì)這些場(chǎng)景進(jìn)行保護(hù),否則它肯定是不安全的。通常,您將使用簡(jiǎn)單的Web層安全性來(lái)對(duì)基本URL應(yīng)用約束,并在服務(wù)層接口上使用更具體的基于方法的安全性來(lái)真正確定允許的內(nèi)容。
如果拋出了 AccessDeniedException?,并且用戶已經(jīng)通過(guò)了身份驗(yàn)證,那么這意味著這個(gè)用戶嘗試了一個(gè)沒(méi)有足夠權(quán)限的操作。在這種情況下,ExceptionTranslationFilter?將調(diào)用第二個(gè)策略,即?AccessDeniedHandler。默認(rèn)情況下,使用 AccessDeniedHandlerImpl?,它只向客戶機(jī)發(fā)送403(禁止)響應(yīng)?;蛘撸梢燥@式配置一個(gè)實(shí)例(如上面的示例所示),并設(shè)置一個(gè)可以被請(qǐng)求轉(zhuǎn)發(fā)到的錯(cuò)誤頁(yè)URL。這可以是一個(gè)簡(jiǎn)單的“拒絕訪問(wèn)”頁(yè)面,如JSP,也可以是一個(gè)更復(fù)雜的處理程序,如MVC控制器。當(dāng)然,您可以自己實(shí)現(xiàn)接口并使用自己的實(shí)現(xiàn)。
還可以在使用命名空間配置應(yīng)用程序時(shí)提供自定義的 AccessDeniedHandler?。有關(guān)詳細(xì)信息,請(qǐng)參閱?the namespace appendix。
SavedRequests 和 RequestCache接口
ExceptionTranslationFilter?的另一個(gè)職責(zé)是在調(diào)用 AuthenticationEntryPoint 之前保存當(dāng)前請(qǐng)求。這允許在用戶進(jìn)行身份驗(yàn)證后還原請(qǐng)求(請(qǐng)參閱先前的Web身份驗(yàn)證概述)。一個(gè)典型的例子是,用戶使用一個(gè)表單登錄,然后由默認(rèn)的SavedRequestAwareAuthenticationSuccessHandler?(見(jiàn)下文)重定向到原始URL。
RequestCache?封裝了存儲(chǔ)和檢索 HttpServletRequest? 實(shí)例所需的功能。默認(rèn)情況下,使用HttpSessionRequestCache?,它將請(qǐng)求存儲(chǔ)在 HttpSession?中。當(dāng)用戶被重定向到原始URL時(shí),RequestCacheFilter的任務(wù)是從緩存中實(shí)際還原保存的請(qǐng)求。
在正常情況下,您不需要修改任何此功能,但保存的請(qǐng)求處理是一種“盡最大努力”的方法,并且可能存在默認(rèn)配置無(wú)法處理的情況。這些接口的使用使得它可以從SpringSecurity3.0開(kāi)始完全插入。
10.2.3 安全上下文持久性篩選器?SecurityContextPersistenceFilter
我們?cè)诩夹g(shù)概述一章中介紹了這個(gè)非常重要的過(guò)濾器的用途,因此您可能希望在此時(shí)重新閱讀該部分。讓我們先看看如何配置它與 FilterChainProxy 一起使用?;九渲弥恍枰猙ean本身。
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
正如我們之前看到的,這個(gè)過(guò)濾器有兩個(gè)主要任務(wù)。它負(fù)責(zé)在HTTP請(qǐng)求之間存儲(chǔ)SecurityContext內(nèi)容,并在請(qǐng)求完成時(shí)清除 SecurityContextHolder。清除存儲(chǔ)上下文的 ThreadLocal?是非常重要的,因?yàn)榫€程可能會(huì)被替換到servlet容器的線程池中,而特定用戶的安全上下文仍然附加。該線程隨后可能會(huì)在后期使用,使用錯(cuò)誤的憑據(jù)執(zhí)行操作。
安全上下文存儲(chǔ)庫(kù)?SecurityContextRepository
從SpringSecurity3.0開(kāi)始,加載和存儲(chǔ)安全上下文的工作現(xiàn)在被委托給一個(gè)單獨(dú)的策略接口:
public interface SecurityContextRepository {
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
void saveContext(SecurityContext context, HttpServletRequest request,
? ? ? ? HttpServletResponse response);
}
HttpRequestResponseHolder?只是接收請(qǐng)求和響應(yīng)對(duì)象的容器,允許用包裝類(lèi)的實(shí)現(xiàn)來(lái)替換這些對(duì)象。返回的內(nèi)容將傳遞到過(guò)濾器鏈。
默認(rèn)實(shí)現(xiàn)是 HttpSessionSecurityContextRepository,它將安全上下文存儲(chǔ)為 HttpSession?屬性。此實(shí)現(xiàn)最重要的配置參數(shù)是 allowSessionCreation?屬性,該屬性默認(rèn)為 true,從而允許類(lèi)在需要會(huì)話時(shí)為經(jīng)過(guò)身份驗(yàn)證的用戶存儲(chǔ)安全上下文時(shí)創(chuàng)建 session(除非進(jìn)行了身份驗(yàn)證,并且安全上下文的內(nèi)容發(fā)生了改變,否則不會(huì)創(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>
或者,你可以提供一個(gè) NullSecurityContextRepository 實(shí)例,一個(gè)空對(duì)象實(shí)現(xiàn),它將阻止存儲(chǔ)安全上下文,即使在請(qǐng)求期間已經(jīng)創(chuàng)建了會(huì)話。
10.2.4?UsernamePasswordAuthenticationFilter
我們現(xiàn)在看到了三個(gè)主要的過(guò)濾器,它們總是出現(xiàn)在Spring Security Web配置中。這三個(gè)元素也是由namespace <http>?元素自動(dòng)創(chuàng)建的,不能用替代項(xiàng)替換?,F(xiàn)在唯一缺少的是一個(gè)實(shí)際的身份驗(yàn)證機(jī)制,它允許用戶進(jìn)行身份驗(yàn)證。此篩選器是最常用的身份驗(yàn)證篩選器,也是最常用的自定義篩選器。它還提供由命名空間中的<form login>元素使用的實(shí)現(xiàn)。配置它需要三個(gè)階段。
?像上面所做的那樣,使用登錄頁(yè)面的URL配置一個(gè)LoginLauthenAuthenticationEntryPoint,并將其設(shè)置在ExceptionTranslationFilter上。
?實(shí)現(xiàn)登錄頁(yè)面(使用JSP或MVC控制器)。
?在應(yīng)用程序上下文中配置 UsernamePasswordAuthenticationFilter? 的實(shí)例
?將過(guò)濾器bean添加到您的過(guò)濾器鏈代理(確保您關(guān)注順序)。
登錄表單只包含用戶名和密碼輸入字段,并發(fā)布到由過(guò)濾器監(jiān)控的URL(默認(rèn)為/login)?;镜倪^(guò)濾器配置如下所示:
<bean id="authenticationFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
Application Flow on Authentication Success and Failure
過(guò)濾器調(diào)用配置的 AuthenticationManager?來(lái)處理每個(gè)身份驗(yàn)證請(qǐng)求。成功身份驗(yàn)證或身份驗(yàn)證失敗后的目標(biāo)分別由AuthenticationSuccessHandler?和 AuthenticationFailureHandler?策略接口控制。過(guò)濾器具有允許您設(shè)置屬性,因此您可以完全自定義行為。提供了一些標(biāo)準(zhǔn)實(shí)現(xiàn),如 SimpleUrlAuthenticationSuccessHandler、SavedRequestAwareAuthenticationSuccessHandler、SimpleUrlAuthenticationFailureHandler、ExceptionMappingAuthenticationFailureHandler?和 DelegatingAuthenticationFailureHandler。多了解這些類(lèi)的javadoc,以及?AbstractAuthenticationProcessingFilter?,了解它們的工作原理和支持的功能。
如果身份驗(yàn)證成功,則生成的?Authentication?將放入SecurityContextHolder。然后,將調(diào)用配置的AuthenticationSuccessHandler?,將用戶重定向或轉(zhuǎn)發(fā)到適當(dāng)?shù)哪繕?biāo)。默認(rèn)情況下,會(huì)使用SavedRequestAwareAuthenticationSuccessHandler?,這意味著用戶將被重定向到請(qǐng)求登錄之前所請(qǐng)求的原始目標(biāo)。
ExceptionTranslationFilter?緩存用戶發(fā)出的原始請(qǐng)求。當(dāng)用戶進(jìn)行身份驗(yàn)證時(shí),請(qǐng)求處理程序使用此緩存請(qǐng)求獲取原始URL并重定向到該URL。然后重新構(gòu)建原始請(qǐng)求并將其用作替代請(qǐng)求。
如果身份驗(yàn)證失敗,將調(diào)用配置的 AuthenticationFailureHandler?。