spring security

在Web應用程序中的身份驗證

現(xiàn)在讓我們來看看你在Web應用程序中使用Spring Security的情況(不啟用web.xml安全性)廓鞠。用戶如何進行身份驗證和建立安全環(huán)境?

考慮一個典型的Web應用程序的身份驗證過程:

  1. 你訪問首頁, 點擊一個鏈接。

  2. 向服務器發(fā)送一個請求爪幻,服務器判斷你是否在訪問一個受保護的資源丹弱。

  3. 如果你還沒有進行過認證庐船,服務器發(fā)回一個響應昆庇,提示你必須進行認證歇万。響應可能是HTTP響應代碼揩晴,或者是重新定向到一個特定的web頁面。

  4. 依據(jù)驗證機制贪磺,你的瀏覽器將重定向到特定的web頁面硫兰,這樣你可以添加表單,或者瀏覽器使用其他方式校驗你的身份(比如寒锚,一個基本校驗對話框劫映,cookie,或者X509證書刹前,或者其他)泳赋。

  5. 瀏覽器會發(fā)回一個響應給服務器。 這將是HTTP POST包含你填寫的表單內容腮郊,或者是HTTP頭部摹蘑,包含你的驗證信息。

  6. 下一步轧飞,服務器會判斷當前的證書是否是有效的衅鹿, 如果他們是有效的撒踪,下一步會執(zhí)行。 如果他們是非法的大渤,通常你的瀏覽器會再嘗試一次(所以你返回的步驟二)制妄。

  7. 你發(fā)送的原始請求,會導致重新嘗試驗證過程泵三。有希望的是耕捞,你會通過驗證,得到足夠的授權烫幕,訪問被保護的資源俺抽。如果你有足夠的權限,請求會成功较曼。否則钝荡,你會收到一個HTTP錯誤代碼403必逆,意思是訪問被拒絕柠并。

Spring Security使用鮮明的類負責上面提到的每個步驟澜公。主要的部分是(為了使用他們) ExceptionTranslationFilter, 一個 AuthenticationEntryPoint 一個驗證機制, 我們在上一節(jié)看到它負責調用AuthenticationManager

ExceptionTranslationFilter

ExceptionTranslationFilter是一個Spring Security過濾器萍歉,用來檢測是否拋出了Spring Security異常侣颂。這些異常會被AbstractSecurityInterceptor拋出,它主要用來提供驗證服務枪孩。我們會在下一節(jié)討論AbstractSecurityInterceptor憔晒,但是現(xiàn)在,我們只需要知道蔑舞,它是用來生成Java丛晌,并且要知道和HTTP沒什么關系,或者如何驗證一個主體斗幼。而ExceptionTranslationFilter提供這些服務澎蛛,使用特點那個的響應,返回錯誤代碼403(如果主體被驗證了蜕窿,但是權限不足-在上邊的步驟七),或者啟動一個AuthenticationEntryPoint(如果主體沒有被認證谋逻,然后我們需要進入步驟三)。

AuthenticationEntryPoint

AuthenticationEntryPoint對應上面列表中的步驟三桐经。如你所想的毁兆,每個web應用程序都有默認的驗證策略(好的,這可以在Spring Security里配置一切阴挣,但是讓我們現(xiàn)在保持簡單)气堕。每個主要驗證系統(tǒng)會有它自己的AuthenticationEntryPoint實現(xiàn), 會執(zhí)行動作,如同步驟三里的描述一樣茎芭。

驗證機制

在你的瀏覽器決定提交你的認證證書之后(使用HTTP表單發(fā)送或者是HTTP頭),服務器部分需要有一些東西來"收集"這些驗證信息∫灸ぃ現(xiàn)在我們到了上述的第六步。 在Spring Security里梅桩,我們需要一個特定的名字壹粟,來描述從用戶代碼(通常是瀏覽器)收集驗證信息的功能,這個名字就是"驗證機制"宿百。實例是窗體的基本登錄和基本的身份驗證趁仙。一旦認證細節(jié)已從用戶代理收集,建立一個Authentication "request"對象,然后提交給AuthenticationManager垦页。

驗證機制重新獲得了組裝好的Authentication對象時雀费,它會認為請求有效,把Authentication放到SecurityContextHolder里的痊焊,然后導致原始請求重審(第七步)坐儿。另一方面,如果AuthenticationManager駁回了請求,驗證機制會讓用戶代碼重試(第二步)。

Storing the SecurityContext between requests

根據(jù)不同的應用程序類型宋光,在用戶操作的過程中需要有合適的策略來保存security信息。在一個典型的web應用中炭菌,一個用戶登錄系統(tǒng)之后就會被一個特有的session Id所唯一標識,服務器會將session作用期間的principal數(shù)據(jù)保存在緩存中罪佳。在Spring Security中,保存SecurityContext的任務落在了SecurityContextPersistenceFilter身上,它默認將上下文當做HttpSession屬性保存在HTTP請求中,并且將每一個請求的上下文保存在SecurityContextHolder中黑低,最重要的功能,是在請求結束之后,清理SecurityContextHolder赘艳。你不需要處于安全的目的直接和HttpSession打交道。在這里僅僅只是不需要那樣做-總是使用SecurityContextHolder來代替HttpSession克握。

許多其他的應用(舉個例子:一個無狀態(tài)的RESTful風格web服務)不使用Http Session并且每次請求過來都會進行驗證蕾管。然而比較重要的是:SecurityContextPersistenceFilter被包含在過濾器鏈中,并確保每次請求完畢之后清理SecurityContextHolder

| |

其中有一個應用程序接收一個會話的并發(fā)請求,同樣的SecurityContext實例將線程之間共享菩暗。即使正在使用ThreadLocal,它是相同的實例,從每個線程的HttpSession檢索掰曾。如果你希望暫時改變一個線程正在運行的上下文這很有意義。如果你只是使用SecurityContextHolder.getContext(),和調用setAuthentication(anAuthentication)返回的上下文對象,那么Authentication對象將在全部并發(fā)線程共享相同的SecurityContext情況的變化停团。 你可以自定義SecurityContextPersistenceFilter的行為,為每一個請求創(chuàng)建一個完全新的SecurityContext,防止在一個線程的變化影響另一個旷坦。或者,你可以創(chuàng)建一個新的實例,只是在這個點上,你暫時改變了背景佑稠。方法SecurityContextHolder.createEmptyContext()總是返回一個新的上下文實例秒梅。

Spring Security的訪問控制(授權)

負責Spring Security訪問控制決策的主要接口是AccessDecisionManager。它有一個decide方法舌胶,它需要一個Authentication對象請求訪問,一個"secure object"(見下文)和安全元數(shù)據(jù)屬性的列表適用的對象(如一個列表哪些角色需要被訪問授權)捆蜀。

安全對象和AbstractSecurityInterceptor

那么什么一個"安全對象"呢?Spring Security使用術語是指可以有安全性的任何對象(如授權決策)應用于它。最常見的例子就是方法調用和web請求。

Spring Security支持的每個安全對象類型都有它自己的類型,他們都是AbstractSecurityInterceptor的子類辆它。很重要的是,如果主體是已經通過了驗證,在AbstractSecurityInterceptor被調用的時候,SecurityContextHolder將會包含一個有效的Authentication誊薄。

AbstractSecurityInterceptor提供了一套一致的工作流程,來處理對安全對象的請求娩井,通常是:

  1. 查找當前請求里分配的"配置屬性"暇屋。

  2. 把安全對象,當前的Authentication和配置屬性,提交給AccessDecisionManager來進行以此認證決定洞辣。

  3. 有可能在調用的過程中,對Authentication進行修改咐刨。

  4. 允許安全對象進行處理(假設訪問被允許了)。

  5. 在調用返回的時候執(zhí)行配置的AfterInvocationManager扬霜。如果調用引發(fā)異常,AfterInvocationManager將不會被調用定鸟。

配置屬性是什么?

一個"配置屬性"可以看做是一個字符串,它對于AbstractSecurityInterceptor使用的類是有特殊含義的。它們由框架內接口ConfigAttribute表示著瓶。它們可能是簡單的角色名稱或擁有更復雜的含義,這就與AccessDecisionManager實現(xiàn)的先進程度有關了联予。AbstractSecurityInterceptor和配置在一起的 SecurityMetadataSource 用來為一個安全對象搜索屬性。通常這個屬性對用戶是不可見的材原。配置屬性將以注解的方式設置在受保護方法上沸久,或者作為受保護URLs的訪問屬性。例如,當我們看到像<intercept-url pattern='/secure/**' access='ROLE_A,ROLE_B'/>命名空間中的介紹,這是說配置屬性ROLE_AROLE_B適用于匹配Web請求的特定模式余蟹。在實踐中,使用默認的AccessDecisionManager配置, 這意味著,任何人誰擁有GrantedAuthority只要符合這兩個屬性將被允許訪問卷胯。嚴格來說,它們只是依賴于AccessDecisionManager實施的屬性和解釋。使用前綴ROLE_是一個標記,以表明這些屬性是角色,應該由Spring Security的RoleVoter前綴被消耗掉威酒。這只是使用AccessDecisionManager的選擇基礎窑睁。我們將在授權章看到AccessDecisionManager是如何實現(xiàn)的。

RunAsManager

假設AccessDecisionManager決定允許執(zhí)行這個請求,AbstractSecurityInterceptor會正常執(zhí)行這個請求葵孤。話雖如此担钮,罕見情況下,用戶可能需要把SecurityContextAuthentication換成另一個Authentication, 這是由AccessDecisionManager 調用RunAsManager尤仍。這也許在,有原因,不常見的情況下有用,比如服務層方法需要調用遠程系統(tǒng)表現(xiàn)不同的身份箫津。 因為Spring Security自動傳播安全身份,從一個服務器到另一個(假設你使用了配置好的RMI或者HttpInvoker遠程調用協(xié)議客戶端)宰啦,就可以用到它了鲤嫡。

AfterInvocationManager

按照下面安全對象執(zhí)行和返回的方式-可能意味著完全的方法調用或過濾器鏈的執(zhí)行-在AbstractSecurityInterceptor得到一個最后的機會來處理調用。這種狀態(tài)下AbstractSecurityInterceptor對有可能修改返回對象感興趣绑莺。你可能想讓它發(fā)生暖眼,因為驗證決定不能“關于如何在”一個安全對象調用。高可插拔性,AbstractSecurityInterceptor通過控制AfterInvocationManager在實際需要的時候修改對象纺裁。這里類實際上可能替換對象诫肠,或者拋出異常司澎,或者什么也不做。如果調用成功后栋豫,檢查調用才會執(zhí)行挤安。如果出現(xiàn)異常,額外的檢查將被跳過丧鸯。

AbstractSecurityInterceptor 和它的相關對象 Security interceptors and the "secure object" model

Abstract Security Interceptor

Figure 1. Security interceptors and the "secure object" model

擴展安全對象模型

只有當開發(fā)人員考慮一個全新的攔截方法和授權請求時才需要直接使用安全對象蛤铜。例如,為了確保對消息系統(tǒng)的調用丛肢,它有可能建立建立一個新的安全對象围肥。任何東西都需要安全,并且還提供了一種方法去調用(如建議語義的AOP)能夠被做成一個安全對象蜂怎。不得不說的是穆刻,大多數(shù)Spring應用程序將只使用三種目前完全支持的安全對象類型(AOP Alliance MethodInvocation, AspectJ JoinPoint和web請求FilterInvocation)。

核心服務

現(xiàn)在杠步,我們對Spring Security的架構和核心類進行高級別的概述氢伟,讓我們在一個或兩個核心接口及其實現(xiàn)的仔細看看,尤其是AuthenticationManager幽歼,UserDetailsServiceAccessDecisionManager這些東西的信息都在這個文檔的里面朵锣,所以這一點很重要,你要知道他們是如何配置如何操作的甸私。

The AuthenticationManager, ProviderManager and AuthenticationProvider

AuthenticationManager只是一個接口诚些,這樣的實現(xiàn)可以是我們選擇的任何東西,但它是如何在實踐中運作的?如果我們需要檢查多個授權數(shù)據(jù)庫或者將不同的授權服務結合起來颠蕴,類似數(shù)據(jù)庫和LDAP服務器?

Spring Security的默認實現(xiàn)被稱為ProviderManager而非處理身份驗證請求本身,它委托給一個列表去配置AuthenticationProvider助析,其中每個查詢反過來犀被,看它是否能進行認證。每個提供程序都將拋出一個異惩饧剑或返回一個完全填充的身份驗證對象寡键。還記得我們的好朋友,UserDetailsUserDetailsService嗎?如果沒有雪隧,回到前面的章節(jié)刷新你的記憶西轩。到驗證的認證請求的最常見的方法是加載相應UserDetails并針對已經由用戶輸入所述一個檢查加載密碼。這是由DaoAuthenticationProvider所使用的方法(見下文)脑沿。加載的UserDetails對象-尤其是GrantedAuthority的IT包含-建設是返回一個成功驗證藕畔,并存儲在SecurityContext完全填充Authentication對象時,將被使用庄拇。

如果你使用的命名空間注服,創(chuàng)建并在內部進行維護ProviderManager的一個實例韭邓,您可以通過使用命名空間身份驗證提供元素添加提供商。(see 命名空間章節(jié))溶弟。在這種情況下女淑,你不應該聲明在應用程序上下文中的ProviderManager bean。但是辜御,如果你沒有使用命名空間鸭你,那么你會這樣聲明:

<bean id="authenticationManager"
        class="org.springframework.security.authentication.ProviderManager">
    <constructor-arg>
        <list>
            <ref local="daoAuthenticationProvider"/>
            <ref local="anonymousAuthenticationProvider"/>
            <ref local="ldapAuthenticationProvider"/>
        </list>
    </constructor-arg>
</bean>

在上面的例子中,我們有三個提供者擒权。它們試圖在順序顯示(它是通過使用一個List的暗示)袱巨,每個提供者都能嘗試驗證,或者通過簡單的返回null跳過認證菜拓。如果所有的實現(xiàn)都返回null瓣窄,則ProviderManager將拋出一個ProviderNotFoundException。如果你有興趣了解更多的有關提供者纳鼎,請參考ProviderManager的JavaDocs俺夕。

身份驗證機,如Web表單登錄處理過濾器被注入到ProviderManager的引用贱鄙,將調用它來處理自己的身份驗證請求劝贸。你需要的供應商有時可以與認證機制互換,而在其他時間逗宁,他們將依賴于特定的認證機制映九。例如,DaoAuthenticationProviderLdapAuthenticationProvider給它提交一個簡單的用戶名/密碼驗證請求瞎颗,并因此將與基于表單登錄或HTTP基本驗證工作的機制兼容件甥。另一方面,一些認證機制創(chuàng)建只能由單一類型AuthenticationProvider解釋的認證請求對象哼拔。這一方面的一個例子是JA-SIG CAS引有,它使用一個服務票據(jù)的概念,因此可以僅通過一個CasAuthenticationProvider進行認證倦逐。你不必太在意這一點譬正,因為如果你忘記注冊一個合適的供應商,你會簡單地收到一個ProviderNotFoundException不進行認證的嘗試檬姥。

清楚成功認證的憑據(jù)

默認情況下(從Spring Security 3.1開始)的ProviderManager將試圖清除它返回一個成功的認證請求的Authentication`對象的任何敏感的身份驗證信息曾我。這可以防止密碼等個人資料超過保留時間。

當使用用戶對象的高速緩存時健民,例如抒巢,改善在無狀態(tài)情況下應用程序的性能,這可能導致問題秉犹。如果Authentication包含在高速緩存(諸如UserDetails實例)的對象的引用中虐秦,將其憑證移除平酿,則它將不再能夠進行對緩存的值進行驗證。你需要考慮到這一點悦陋,如果你使用的是高速緩存蜈彼。一個顯而易見的解決方案是讓一個對象的副本,無論是在高速緩存中執(zhí)行或在AuthenticationProvider它創(chuàng)建返回Authentication對象俺驶。另外幸逆,你可以在ProviderManager中禁用eraseCredentialsAfterAuthentication。查看Javadoc了解更多信息暮现。

DaoAuthenticationProvider

Spring Security中實現(xiàn)最簡單的AuthenticationProviderDaoAuthenticationProvider还绘,也是最早支持的框架。它利用了UserDetailsService(作為DAO)去查找用戶名和密碼栖袋。它的用戶進行身份驗證通過userdetailsservice加載usernamepasswordauthenticationtoken提交密碼進行一對一的比較拍顷。配置提供程序是非常簡單的:

<bean id="daoAuthenticationProvider"
    class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>

這個PasswordEncoder是可選的。一個PasswordEncoder提供編碼以及UserDetails對象提出的密碼是從配置UserDetailsService返回的解碼塘幅。 這將更加詳細 如下昔案。

UserDetailsService實現(xiàn)

本參考指南早些時候提到的,大多數(shù)的認證供應商利用的userdetailsuserdetailsservice接口电媳√ごВ回想一下,UserDetailsService是一個方法:

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

返回的UserDetails是提供給getters的一個接口匾乓,以保證非空的認證信息捞稿,例如,用戶名拼缝,密碼娱局,授權和用戶帳戶是否被啟用或禁用。大多數(shù)認證供應商將使用UserDetailsService咧七,即使用戶名和密碼不作為認證決定的一部分衰齐。他們可以使用返回的UserDetails對象為其GrantedAuthority信息對象,因為其他的一些系統(tǒng)(如LDAP或X.509或CAS等)承擔了實際驗證憑證的的責任猪叙。

鑒于UserDetailsService就是這么簡單實現(xiàn)的娇斩,它應該便于用戶檢索使用自己選擇的持久化策略的認證信息仁卷。話雖如此穴翩,Spring Security確實包括了許多有用的基本實現(xiàn),我們將在下面看到锦积。

在內存認證

簡單的使用去創(chuàng)建一個自定義的UserDetailsService實現(xiàn)選擇從一個持久性引擎中提取信息芒帕,但許多應用程序不需要這么復雜。尤其是如果你正在建設一個原型應用或剛剛開始結合Spring Security當你真的不想花時間配置數(shù)據(jù)庫或寫作userdetailsservice實現(xiàn)丰介。對于這種情況背蟆,一個簡單的選項是使用安全性 命名空間user-service元素:

<user-service id="userDetailsService">
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>

這也支持一個外部屬性文件的使用:

<user-service id="userDetailsService" properties="users.properties"/>

屬性文件應包含在表單條目

username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]

例如

jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
bob=bobspassword,ROLE_USER,enabled

JdbcDaoImpl

Spring Security還包括UserDetailsService鉴分,它可以從一個JDBC數(shù)據(jù)源獲得認證信息。內部Spring JDBC的使用带膀,避免了一個全功能對象關系映射(ORM)的復雜性來存儲用戶信息志珍。如果你的應用程序不使用ORM工具,你可以寫一個自定義UserDetailsService重用在你可能已經創(chuàng)建好的映射文件上垛叨÷着矗回到 JdbcDaoImpl,實例的配置如下::

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>

<bean id="userDetailsService"
    class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>

您可以通過修改上面的DriverManagerDataSource使用不同的關系型數(shù)據(jù)庫管理系統(tǒng)嗽元。你也可以從JNDI獲得敛纲,與任何其他的Spring配置使用一個全球性的數(shù)據(jù)源。

Authority Groups

默認情況下剂癌,JdbcDaoImpl加載權限直接映射到用戶的角色(見 數(shù)據(jù)庫架構附錄)淤翔。另一種方法是將權限分成組并分配組給用戶。有些人喜歡這種方式作為管理用戶權限的一種手段佩谷。見 JdbcDaoImpl Javadoc獲得如何能夠使用權限組的更多信息旁壮。該組架構也包括在附錄中。

  1. Core Security Filters
    There are some key filters which will always be used in a web application which uses Spring Security, so we’ll look at these and their supporting classes and interfaces first. We won’t cover every feature, so be sure to look at the Javadoc for them if you want to get the complete picture.

15.1 FilterSecurityInterceptor

We’ve already seen FilterSecurityInterceptor briefly when discussing access-control in general, and we’ve already used it with the namespace where the <intercept-url> elements are combined to configure it internally. Now we’ll see how to explicitly configure it for use with a FilterChainProxy, along with its companion filter ExceptionTranslationFilter. A typical configuration example is shown below:

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;"><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></pre>

FilterSecurityInterceptor is responsible for handling the security of HTTP resources. It requires a reference to an AuthenticationManager and an AccessDecisionManager. It is also supplied with configuration attributes that apply to different HTTP URL requests. Refer back to the original discussion on these in the technical introduction.

The FilterSecurityInterceptor can be configured with configuration attributes in two ways. The first, which is shown above, is using the <filter-security-metadata-source> namespace element. This is similar to the <http> element from the namespace chapter but the <intercept-url> child elements only use the pattern and access attributes. Commas are used to delimit the different configuration attributes that apply to each HTTP URL. The second option is to write your own SecurityMetadataSource, but this is beyond the scope of this document. Irrespective of the approach used, the SecurityMetadataSource is responsible for returning a List<ConfigAttribute> containing all of the configuration attributes associated with a single secure HTTP URL.

It should be noted that the FilterSecurityInterceptor.setSecurityMetadataSource() method actually expects an instance of FilterInvocationSecurityMetadataSource. This is a marker interface which subclasses SecurityMetadataSource. It simply denotes the SecurityMetadataSource understands FilterInvocation s. In the interests of simplicity we’ll continue to refer to the FilterInvocationSecurityMetadataSource as a SecurityMetadataSource, as the distinction is of little relevance to most users.

The SecurityMetadataSource created by the namespace syntax obtains the configuration attributes for a particular FilterInvocation by matching the request URL against the configured pattern attributes. This behaves in the same way as it does for namespace configuration. The default is to treat all expressions as Apache Ant paths and regular expressions are also supported for more complex cases. The request-matcher attribute is used to specify the type of pattern being used. It is not possible to mix expression syntaxes within the same definition. As an example, the previous configuration using regular expressions instead of Ant paths would be written as follows:

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;"><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></pre>

Patterns are always evaluated in the order they are defined. Thus it is important that more specific patterns are defined higher in the list than less specific patterns. This is reflected in our example above, where the more specific /secure/super/ pattern appears higher than the less specific /secure/ pattern. If they were reversed, the /secure/ pattern would always match and the /secure/super/ pattern would never be evaluated.

15.2 ExceptionTranslationFilter

The ExceptionTranslationFilter sits above the FilterSecurityInterceptor in the security filter stack. It doesn’t do any actual security enforcement itself, but handles exceptions thrown by the security interceptors and provides suitable and HTTP responses.

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;"><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></pre>

15.2.1 AuthenticationEntryPoint

The AuthenticationEntryPoint will be called if the user requests a secure HTTP resource but they are not authenticated. An appropriate AuthenticationException or AccessDeniedException will be thrown by a security interceptor further down the call stack, triggering the commence method on the entry point. This does the job of presenting the appropriate response to the user so that authentication can begin. The one we’ve used here is LoginUrlAuthenticationEntryPoint, which redirects the request to a different URL (typically a login page). The actual implementation used will depend on the authentication mechanism you want to be used in your application.

15.2.2 AccessDeniedHandler

What happens if a user is already authenticated and they try to access a protected resource? In normal usage, this shouldn’t happen because the application workflow should be restricted to operations to which a user has access. For example, an HTML link to an administration page might be hidden from users who do not have an admin role. You can’t rely on hiding links for security though, as there’s always a possibility that a user will just enter the URL directly in an attempt to bypass the restrictions. Or they might modify a RESTful URL to change some of the argument values. Your application must be protected against these scenarios or it will definitely be insecure. You will typically use simple web layer security to apply constraints to basic URLs and use more specific method-based security on your service layer interfaces to really nail down what is permissible.

If an AccessDeniedException is thrown and a user has already been authenticated, then this means that an operation has been attempted for which they don’t have enough permissions. In this case, ExceptionTranslationFilter will invoke a second strategy, the AccessDeniedHandler. By default, an AccessDeniedHandlerImpl is used, which just sends a 403 (Forbidden) response to the client. Alternatively you can configure an instance explicitly (as in the above example) and set an error page URL which it will forwards the request to [11]. This can be a simple "access denied" page, such as a JSP, or it could be a more complex handler such as an MVC controller. And of course, you can implement the interface yourself and use your own implementation.

It’s also possible to supply a custom AccessDeniedHandler when you’re using the namespace to configure your application. See the namespace appendix for more details.

15.2.3 SavedRequest s and the RequestCache Interface

Another responsibility of ExceptionTranslationFilter responsibilities is to save the current request before invoking the AuthenticationEntryPoint. This allows the request to be restored after the user has authenticated (see previous overview of web authentication). A typical example would be where the user logs in with a form, and is then redirected to the original URL by the default SavedRequestAwareAuthenticationSuccessHandler (see below).

The RequestCache encapsulates the functionality required for storing and retrieving HttpServletRequest instances. By default the HttpSessionRequestCache is used, which stores the request in the HttpSession. The RequestCacheFilter has the job of actually restoring the saved request from the cache when the user is redirected to the original URL.

Under normal circumstances, you shouldn’t need to modify any of this functionality, but the saved-request handling is a "best-effort" approach and there may be situations which the default configuration isn’t able to handle. The use of these interfaces makes it fully pluggable from Spring Security 3.0 onwards.

15.3 SecurityContextPersistenceFilter

We covered the purpose of this all-important filter in the Technical Overview chapter so you might want to re-read that section at this point. Let’s first take a look at how you would configure it for use with a FilterChainProxy. A basic configuration only requires the bean itself

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;"><bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/></pre>

As we saw previously, this filter has two main tasks. It is responsible for storage of the SecurityContext contents between HTTP requests and for clearing the SecurityContextHolder when a request is completed. Clearing the ThreadLocal in which the context is stored is essential, as it might otherwise be possible for a thread to be replaced into the servlet container’s thread pool, with the security context for a particular user still attached. This thread might then be used at a later stage, performing operations with the wrong credentials.

15.3.1 SecurityContextRepository

From Spring Security 3.0, the job of loading and storing the security context is now delegated to a separate strategy interface:

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;">public interface SecurityContextRepository {

SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);

void saveContext(SecurityContext context, HttpServletRequest request,
HttpServletResponse response);
}</pre>

The HttpRequestResponseHolder is simply a container for the incoming request and response objects, allowing the implementation to replace these with wrapper classes. The returned contents will be passed to the filter chain.

The default implementation is HttpSessionSecurityContextRepository, which stores the security context as an HttpSession attribute [12]. The most important configuration parameter for this implementation is the allowSessionCreation property, which defaults to true, thus allowing the class to create a session if it needs one to store the security context for an authenticated user (it won’t create one unless authentication has taken place and the contents of the security context have changed). If you don’t want a session to be created, then you can set this property to false:

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;"><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></pre>

Alternatively you could provide an instance of NullSecurityContextRepository, a null object implementation, which will prevent the security context from being stored, even if a session has already been created during the request.

15.4 UsernamePasswordAuthenticationFilter

We’ve now seen the three main filters which are always present in a Spring Security web configuration. These are also the three which are automatically created by the namespace <http> element and cannot be substituted with alternatives. The only thing that’s missing now is an actual authentication mechanism, something that will allow a user to authenticate. This filter is the most commonly used authentication filter and the one that is most often customized [13]. It also provides the implementation used by the <form-login> element from the namespace. There are three stages required to configure it.

  • Configure a LoginUrlAuthenticationEntryPoint with the URL of the login page, just as we did above, and set it on the ExceptionTranslationFilter.
  • Implement the login page (using a JSP or MVC controller).
  • Configure an instance of UsernamePasswordAuthenticationFilter in the application context
  • Add the filter bean to your filter chain proxy (making sure you pay attention to the order).

The login form simply contains username and password input fields, and posts to the URL that is monitored by the filter (by default this is /login). The basic filter configuration looks something like this:

<pre class="programlisting" style="line-height: 1.4; color: rgb(0, 0, 0); font-size: 15px; padding: 6px 10px; background-color: rgb(248, 248, 248); border: 1px solid rgb(204, 204, 204); border-radius: 3px; clear: both; overflow: auto; font-family: Consolas, "liberation mono", Courier, monospace;"><bean id="authenticationFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean></pre>

15.4.1 Application Flow on Authentication Success and Failure

The filter calls the configured AuthenticationManager to process each authentication request. The destination following a successful authentication or an authentication failure is controlled by the AuthenticationSuccessHandler and AuthenticationFailureHandler strategy interfaces, respectively. The filter has properties which allow you to set these so you can customize the behaviour completely [14]. Some standard implementations are supplied such as SimpleUrlAuthenticationSuccessHandler, SavedRequestAwareAuthenticationSuccessHandler, SimpleUrlAuthenticationFailureHandler, ExceptionMappingAuthenticationFailureHandler and DelegatingAuthenticationFailureHandler. Have a look at the Javadoc for these classes and also for AbstractAuthenticationProcessingFilter to get an overview of how they work and the supported features.

If authentication is successful, the resulting Authentication object will be placed into the SecurityContextHolder. The configured AuthenticationSuccessHandler will then be called to either redirect or forward the user to the appropriate destination. By default a SavedRequestAwareAuthenticationSuccessHandler is used, which means that the user will be redirected to the original destination they requested before they were asked to login.

|
[Note]

|
|

The ExceptionTranslationFilter caches the original request a user makes. When the user authenticates, the request handler makes use of this cached request to obtain the original URL and redirect to it. The original request is then rebuilt and used as an alternative.

|

If authentication fails, the configured AuthenticationFailureHandler will be invoked.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末琳要,一起剝皮案震驚了整個濱河市寡具,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稚补,老刑警劉巖童叠,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異课幕,居然都是意外死亡厦坛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門乍惊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杜秸,“玉大人,你說我怎么就攤上這事润绎∏说” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵莉撇,是天一觀的道長呢蛤。 經常有香客問我,道長棍郎,這世上最難降的妖魔是什么其障? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮涂佃,結果婚禮上励翼,老公的妹妹穿的比我還像新娘蜈敢。我一直安慰自己,他們只是感情好汽抚,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布抓狭。 她就那樣靜靜地躺著,像睡著了一般造烁。 火紅的嫁衣襯著肌膚如雪辐宾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天膨蛮,我揣著相機與錄音叠纹,去河邊找鬼。 笑死敞葛,一個胖子當著我的面吹牛誉察,可吹牛的內容都是我干的。 我是一名探鬼主播惹谐,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼持偏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了氨肌?” 一聲冷哼從身側響起鸿秆,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎怎囚,沒想到半個月后卿叽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡恳守,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年考婴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片催烘。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡沥阱,死狀恐怖,靈堂內的尸體忽然破棺而出伊群,到底是詐尸還是另有隱情考杉,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布舰始,位于F島的核電站崇棠,受9級特大地震影響,放射性物質發(fā)生泄漏蔽午。R本人自食惡果不足惜易茬,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一酬蹋、第九天 我趴在偏房一處隱蔽的房頂上張望及老。 院中可真熱鬧抽莱,春花似錦、人聲如沸骄恶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽僧鲁。三九已至虐呻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寞秃,已是汗流浹背斟叼。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留春寿,地道東北人朗涩。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像绑改,于是被迫代替她去往敵國和親谢床。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容