Web應(yīng)用的安全--翻譯自spring security 5.1.5

????????大多數(shù)Spring Security用戶會在使用HTTP和servlet API的應(yīng)用程序中使用該框架他托。在本章節(jié)翎猛,我們將了解Spring?Security是如何為應(yīng)用程序的Web層提供身份驗(yàn)證和訪問控制功能产徊。我們將查看名稱空間的facade(外觀設(shè)計模式)员串,并查找實(shí)際上是哪些類和接口被組裝了起來以提供Web層的安全盔腔。在某些情況下喷屋,有必要使用傳統(tǒng)的bean配置來提供對配置的完全控制琳拨,因此我們還將了解如何在不使用命名空間的情況下直接配置這些類。

10.1 Security過濾器鏈

? ??????Spring Security的Web基礎(chǔ)結(jié)構(gòu)完全基于標(biāo)準(zhǔn)的servlet過濾器逼蒙。它不在內(nèi)部使用servlet或任何其他基于servlet的框架(如SpringMVC)从绘,因此它沒有強(qiáng)關(guān)聯(lián)到任何特定的Web技術(shù)。它處理HttpServletRequest和HttpServletResponse是牢,而不關(guān)心這些請求是來自瀏覽器僵井、Web服務(wù)客戶機(jī)、HttpInvoker還是Ajax應(yīng)用程序驳棱。

????????Spring Security在內(nèi)部維護(hù)一個過濾器鏈批什,鏈中每個過濾器都有特定的職責(zé),并且鏈中使用和不使用那些過濾器社搅,取決于所請求的服務(wù)驻债。過濾器的順序很重要,因?yàn)樗鼈冎g存在依賴關(guān)系形葬。如果您一直在使用命名空間進(jìn)行配置合呐,那么過濾器將自動為您配置,并且您不必顯式定義任何SpringBean笙以,但有時您希望完全控制安全過濾器鏈淌实,要么因?yàn)槟褂玫氖敲Q空間中不支持的功能,或者您正在使用自定義的類版本。

10.1.1 委托過濾器代理?DelegatingFilterProxy

????????當(dāng)使用servlet過濾器時拆祈,你顯然需要在web.xml中聲明它們恨闪,否則它們將被servlet容器忽略。在Spring Security中放坏,過濾器類也是在應(yīng)用程序上下文中定義的SpringBean咙咽,因此能夠利用Spring豐富的依賴注入工具和生命周期接口。Spring的 DelegatingFilterProxy 提供了web.xml和應(yīng)用程序上下文之間的鏈接淤年。

當(dāng)使用 DelegatingFilterProxy 時钧敞,您將在web.xml文件中看到類似的內(nèi)容:

<filter>

<filter-name>myFilter</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

<filter-name>myFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

請注意,過濾器實(shí)際上是一個 DelegatingFilterProxy互亮,而不是真正實(shí)現(xiàn)過濾器邏輯的類犁享。?DelegatingFilterProxy?所做的是將過濾器的方法委托給一個從Spring應(yīng)用程序上下文獲得的bean。這使得bean能夠從SpringWeb應(yīng)用程序上下文生命周期的支持和配置的靈活性中獲益豹休。bean必須實(shí)現(xiàn)?javax.servlet.Filter炊昆,并且它必須與filter name元素中的名稱相同。有關(guān)詳細(xì)信息威根,請閱讀關(guān)于?DelegatingFilterProxy 的JavaDoc凤巨。

10.1.2 過濾鏈代理?FilterChainProxy?

Spring Security的Web基礎(chǔ)結(jié)構(gòu)只能通過委托給 FilterChainProxy?實(shí)例來使用。?Security?過濾器不應(yīng)該被本身使用洛搀。理論上敢茁,您可以聲明在應(yīng)用程序上下文文件中所需的每個SpringSecurity過濾器bean,并為每個過濾器向web.xml添加一個相應(yīng)的 DelegatingFilterProxy 條目留美,確保它們的順序正確彰檬,但這會很麻煩,如果您有很多過濾器谎砾,這楊還會很快使web.xml文件混亂不堪逢倍。FilterChainProxy?允許我們向web.xml添加一個條目,并全權(quán)處理應(yīng)用程序上下文文件來管理我們的web?security?beans景图。它是使用?DelegatingFilterProxy 連接的较雕,就像上面的例子一樣,但是使用?filter-name?來設(shè)置bean的名稱為“filterChainProxy”挚币。然后過濾器鏈就在應(yīng)用程序上下文中以相同的bean名稱被聲明了亮蒋。這里有個例子:

<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">

<constructor-arg>

????<list>

????<sec:filter-chain pattern="/restful/**" filters="

? ? ? ? securityContextPersistenceFilterWithASCFalse,

? ? ? ? basicAuthenticationFilter,

? ? ? ? exceptionTranslationFilter,

? ? ? ? filterSecurityInterceptor" />

<sec:filter-chain pattern="/**" filters="

? ? ? ? securityContextPersistenceFilterWithASCTrue,

? ? ? ? formLoginFilter,

? ? ? ? exceptionTranslationFilter,

? ? ? ? filterSecurityInterceptor" />

</list>

</constructor-arg>

</bean>

命名空間元素?filter-chain 用于方便設(shè)置應(yīng)用程序中所需的安全篩選器鏈。它將特定的URL映射到由filters元素中指定的bean名稱構(gòu)建的過濾器列表妆毕,并將它們組合到類型為?SecurityFilterChain 的bean中慎玖。pattern屬性采用Ant路徑,最具體的URIs應(yīng)該首先出現(xiàn)笛粘。在運(yùn)行時凄吏,FilterChainProxy?將定位與當(dāng)前Web請求匹配的第一個URI模式远舅,由filters屬性指定的filter bean列表將應(yīng)用于該請求。過濾器將按照定義的順序被調(diào)用痕钢,因此您可以完全控制應(yīng)用于特定URL的過濾器鏈。

您可能已經(jīng)注意到我們在過濾器鏈中聲明了兩個SecurityContextPersistenceFilter(ASC是allowSessionCreation的縮寫序六,是SecurityContextPersistenceFilter?的一個屬性)任连。由于Web服務(wù)永遠(yuǎn)不會在將來的請求中提供JSessionID,因此為此類用戶代理創(chuàng)建 HttpSession?是浪費(fèi)的例诀。如果您有一個需要最大可伸縮性的大容量應(yīng)用程序随抠,我們建議您使用上面所示的方法。對于較小的應(yīng)用程序繁涂,使用單個SecurityContextPersistenceFilter(其默認(rèn)allowSessionCreation為true)可能就足夠了拱她。

注意,FilterChainProxy?不會在配置它的過濾器上調(diào)用標(biāo)準(zhǔn)的過濾器生命周期方法扔罪。我們建議您使用Spring的應(yīng)用程序上下文生命周期接口作為替代方法秉沼,就像對任何其他SpringBean一樣。

當(dāng)我們研究如何使用命名空間配置來設(shè)置Web security 時矿酵,我們使用了一個名為“springSecurityFilterChain”的 DelegatingFilterProxy』8矗現(xiàn)在應(yīng)該可以看到這是由命名空間創(chuàng)建的?FilterChainProxy? 的名稱。

繞過過濾鏈

您可以使用屬性 filters=“none” 作為提供過濾器bean列表的替代方法全肮。這將完全忽略安全過濾器鏈中的請求模式敞咧。請注意,任何與此路徑匹配的內(nèi)容都不會應(yīng)用任何身份驗(yàn)證或授權(quán)服務(wù)辜腺,并且可以自由訪問休建。如果要在請求期間使用SecurityContext?的內(nèi)容,那么它必須通過安全篩選器鏈傳遞评疗。否則测砂,SecurityContextHolder?將不會被填充,內(nèi)容將為空壤巷。典型的例子就是:如果你的登錄頁面時未經(jīng)過濾器鏈過濾的邑彪,那么頁面上將不存在csrfToken的值,那么在系統(tǒng)中開啟csrf(默認(rèn))但未提交csrfToken的情況下胧华,登錄認(rèn)證將不通過寄症。

10.1.3 過濾器排序

過濾器在鏈中定義的順序非常重要。不管您實(shí)際使用的過濾器是什么矩动,順序應(yīng)該如下:

\bullet ?ChannelProcessingFilter有巧,因?yàn)樗赡苄枰囟ㄏ虻狡渌麉f(xié)議

\bullet ?SecurityContextPersistenceFilter,因此可以在Web請求開始時在SecurityContextHolder中設(shè)置SecurityContext悲没,并且可以在Web請求結(jié)束時將對SecurityContext的任何更改復(fù)制到httpSession(準(zhǔn)備用于下一個Web請求)

\bullet ?ConcurrentSessionFilter篮迎,因?yàn)樗褂肧ecurityContextHolder功能,需要更新sessionRegistry以反映來自主體的持續(xù)請求

\bullet 身份驗(yàn)證處理機(jī)制 - UsernamePasswordAuthenticationFilter、CasAuthenticationFilter甜橱、BasicAuthenticationFilter?等-以便可以修改SecurityContextHolder以包含有效的Authentication?請求令牌

\bullet SecurityContextHolderWareRequestFilter逊笆,如果您正在使用它將支持Spring Security?的HttpServletRequestWrapper?安裝到servlet容器中

\bullet JaasApiIntegrationFilter,如果一個 JaasAuthenticationToken?在SecurityContextholder中岂傲,它將作為JaasAuthenticationToken的主體來處理FilterChain?难裆。

\bullet ?RememberMeAuthenticationFilter,這樣镊掖,如果沒有早期的 authentication?處理機(jī)制更新了SecurityContextHolder乃戈,并且請求提供了一個允許執(zhí)行“記住我”服務(wù)的cookie,那么將在那里放置一個合適的記住的 Authentication?對象亩进。

\bullet ?AnonymousAuthenticationFilter症虑,這樣,如果沒有早期的身份驗(yàn)證處理機(jī)制更新SecurityContextHolder归薛,則會在那里放置一個匿名 Authentication?對象谍憔。

\bullet ?ExceptionTranslationFilter,捕獲任何Spring Security?異常苟翻,以便返回HTTP錯誤響應(yīng)或啟動適當(dāng)?shù)腁uthenticationEntryPoint 韵卤。????

\bullet ?FilterSecurityInterceptor,用于保護(hù) Web URIs并在拒絕訪問時引發(fā)異常崇猫。

10.1.4 請求匹配和HttpFirewall

Spring Security?有幾個方面(您定義的模式)針對傳入的請求進(jìn)行測試沈条,以決定如何處理請求。當(dāng) FilterChainProxy? 決定請求應(yīng)該通過哪個過濾器鏈時诅炉,以及當(dāng) FilterSecurityInterceptor? 決定采用哪種安全約束來作用于一個請求的時侯蜡歹,就會發(fā)生這種情況。在根據(jù)您定義的模式進(jìn)行測試時涕烧,了解機(jī)制是什么以及使用什么URL值是很重要的月而。

Servlet規(guī)范為 HttpServletRequest? 定義了幾個屬性,這些屬性可以通過getter方法訪問议纯,并且我們可能希望與之匹配父款。這些屬性是contextPath、servletPath瞻凤、pathInfo?和 queryString憨攒。Spring Security?只對保護(hù)應(yīng)用程序中的路徑感興趣,因此忽略了contextPath?阀参。不幸的是肝集,servlet規(guī)范沒有準(zhǔn)確定義對于特定請求URI,servletPath?和 pathInfo 需要包含什么樣的值蛛壳。例如杏瞻,URL的每個路徑段可能包含參數(shù)所刀,如RFC2396中所定義。規(guī)范沒有明確說明這些是否應(yīng)該包含在servletPath?和 pathInfo?的值中捞挥,并且不同的servlet容器之間的行為不同浮创。當(dāng)應(yīng)用程序部署在不從這些值中刪除路徑參數(shù)的容器中時,就產(chǎn)生了危險砌函,攻擊者可以將它們添加到請求的URL中蒸矛,以導(dǎo)致模式匹配意外成功或失敗。傳入URL中的其他變體也是可能的胸嘴。例如,它可以包含路徑遍歷序列(如 /../)或多個正斜杠(//)斩祭,這也可能導(dǎo)致模式匹配失敗劣像。一些容器在執(zhí)行servlet映射之前將其規(guī)范化,但其他容器則沒有這樣做摧玫。為了防止此類問題耳奕,FilterChainProxy?使用HttpFirewall?策略檢查和包裝請求。默認(rèn)情況下诬像,未規(guī)范化的請求被自動拒絕屋群,為了匹配的目的,路徑參數(shù)和重復(fù)斜杠被刪除坏挠。因此芍躏,必須使用 FilterChainProxy?來管理安全過濾器鏈。請注意降狠,servletpath和pathinfo值是由容器解碼的对竣,因此您的應(yīng)用程序不應(yīng)該有任何包含分號的有效路徑,因?yàn)闉榱诉M(jìn)行匹配榜配,這些部分將被刪除否纬。

如上所述,默認(rèn)策略是使用Ant風(fēng)格的路徑進(jìn)行匹配蛋褥,這可能是大多數(shù)用戶的最佳選擇临燃。該策略在類AntPathRequestMatcher?中實(shí)現(xiàn),該類使用Spring的 antPathMatcher 對連接的servletPath和pathinfo執(zhí)行不區(qū)分大小寫的模式匹配烙心,而忽略queryString膜廊。

如果出于某種原因,需要更強(qiáng)大的匹配策略弃理,可以使用正則表達(dá)式溃论。策略實(shí)現(xiàn)類是 RegexRequestMatcher。有關(guān)更多信息痘昌,請參閱此類的javadoc钥勋。

在實(shí)踐中炬转,建議您在服務(wù)層使用方法安全來控制對應(yīng)用程序的訪問,并且不要完全依賴于在Web應(yīng)用程序級別定義的安全約束的使用算灸。URLs會發(fā)生變化扼劈,很難考慮應(yīng)用程序可能支持的所有可能的URL以及如何處理這些請求。您應(yīng)該嘗試并限制自己使用一些簡單易懂的Ant路徑菲驴。始終嘗試使用“deny-by-default”方法荐吵,其中您具有最后定義的catch all通配符(/ or)并拒絕訪問。

在服務(wù)層定義的安全性要強(qiáng)大得多赊瞬,而且很難繞過先煎,所以您應(yīng)該始終利用Spring安全性的方法安全選項(xiàng)。

HttpFirewall?還通過拒絕HTTP響應(yīng)頭中的新行字符來防止HTTP響應(yīng)拆分巧涧。

默認(rèn)情況下使用StrictHttpFirewall?薯蝎。此實(shí)現(xiàn)拒絕看似惡意的請求。如果它對您的需求太嚴(yán)格谤绳,那么您可以自定義拒絕哪些類型的請求占锯。但是,重要的是要知道這樣做可以使應(yīng)用程序抵御攻擊缩筛。例如消略,如果希望利用SpringMVC的矩陣變量,可以在XML中使用以下配置:

<b:beanid="httpFirewall"class="org.springframework.security.web.firewall.StrictHttpFirewall"p:allowSemicolon="true"/><http-firewallref="httpFirewall"/>

同樣的事情可以通過暴露一個嚴(yán)格的防火墻防火墻來實(shí)現(xiàn)瞎抛。

@Bean

publicStrictHttpFirewall httpFirewall() {?

?????StrictHttpFirewall firewall =newStrictHttpFirewall();?

?????firewall.setAllowSemicolon(true);

????returnfirewall;

}

StrictHttpFirewall?提供了一個有效的HTTP方法白名單艺演,這些方法可以防止跨站點(diǎn)跟蹤(XST)和HTTP動詞篡改。默認(rèn)的有效方法是“DELETE”婿失、“GET”钞艇、“HEAD”、“OPTIONS”豪硅、“PATCH”哩照、“POST”和“PUT”。如果您的應(yīng)用程序需要修改有效的方法懒浮,您可以配置一個自定義的StrictHttpFirewall?bean飘弧。例如,以下僅允許http“GET”和“POST”方法:

<b:beanid="httpFirewall"class="org.springframework.security.web.firewall.StrictHttpFirewall"p:allowedHttpMethods="GET,HEAD"/>

<http-firewallref="httpFirewall"/>

同樣的事情可以采用java配置的方式砚著,通過暴露一個StrictHttpFirewall bean來實(shí)現(xiàn)次伶。

@Bean

publicStrictHttpFirewall httpFirewall() {? ??

????StrictHttpFirewall firewall = newStrictHttpFirewall();? ? ????firewall.setAllowedHttpMethods(Arrays.asList("GET","POST"));

????returnfirewall;

}

如果您正在使用新的mockhttpservletrequest(),它當(dāng)前會創(chuàng)建一個HTTP方法作為空字符串“”稽穆。這是一個無效的HTTP方法冠王,將被Spring安全性拒絕。您可以通過用新的mockhttpservletrequest(“get”舌镶,)替換它來解決這個問題柱彻。請參閱SPR U 16851了解請求改進(jìn)此問題的問題豪娜。

如果必須允許任何HTTP方法(不推薦),則可以使用 StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true).哟楷。這將完全禁用HTTP方法的驗(yàn)證瘤载。

10.1.5 與其他基于過濾器的框架一起使用

如果正在使用的其他框架也是基于過濾器的,那么您需要確保請求首先經(jīng)過Spring Security?過濾器卖擅。這使SecurityContextHolder?能夠及時被填充以供其他篩選器使用鸣奔。例如,使用SiteMesh來裝飾網(wǎng)頁惩阶,或者使用Wicket這樣的Web框架來處理其請求挎狸。

10.1.6 高級命名空間配置

正如我們在前面的命名空間章節(jié)中看到的,可以使用多個 http?元素為不同的URL模式定義不同的安全配置断楷。每個元素在內(nèi)部 FilterChainProxy?和應(yīng)該映射到它的url模式中創(chuàng)建一個過濾器鏈伟叛。元素將按聲明的順序添加,因此必須首先聲明最具體的模式脐嫂。這里是另一個例子,對于與上面類似的情況紊遵,應(yīng)用程序既支持無狀態(tài)的RESTfulAPI账千,也支持用戶使用表單登錄的普通Web應(yīng)用程序。

<!-- Stateless RESTful service using Basic authentication -->

<http pattern="/restful/**" create-session="stateless">

<intercept-url pattern='/**' access="hasRole('REMOTE')" />

<http-basic />

</http>

<!-- Empty filter chain for the login page -->

<http pattern="/login.htm*" security="none"/>

<!-- Additional filter chain for normal users, matching all other requests -->

<http>

<intercept-url pattern='/**' access="hasRole('USER')" />

<form-login login-page='/login.htm' default-target-url="/home.htm"/>

<logout />

</http>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暗膜,一起剝皮案震驚了整個濱河市匀奏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌学搜,老刑警劉巖娃善,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瑞佩,居然都是意外死亡聚磺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門炬丸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘫寝,“玉大人,你說我怎么就攤上這事稠炬』腊ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵首启,是天一觀的道長暮屡。 經(jīng)常有香客問我,道長毅桃,這世上最難降的妖魔是什么褒纲? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任准夷,我火速辦了婚禮,結(jié)果婚禮上外厂,老公的妹妹穿的比我還像新娘冕象。我一直安慰自己,他們只是感情好汁蝶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布渐扮。 她就那樣靜靜地躺著,像睡著了一般掖棉。 火紅的嫁衣襯著肌膚如雪墓律。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天幔亥,我揣著相機(jī)與錄音耻讽,去河邊找鬼。 笑死帕棉,一個胖子當(dāng)著我的面吹牛针肥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播香伴,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼慰枕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了即纲?” 一聲冷哼從身側(cè)響起具帮,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎低斋,沒想到半個月后蜂厅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膊畴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年掘猿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唇跨。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡术奖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出轻绞,到底是詐尸還是另有隱情采记,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布政勃,位于F島的核電站唧龄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奸远。R本人自食惡果不足惜既棺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一讽挟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丸冕,春花似錦耽梅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至佩番,卻和暖如春众旗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趟畏。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工贡歧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赋秀。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓利朵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猎莲。 傳聞我的和親對象是個殘疾皇子哗咆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容