Spring in Action中文版(第二版)-Spring Security.docx_第1頁
Spring in Action中文版(第二版)-Spring Security.docx_第2頁
Spring in Action中文版(第二版)-Spring Security.docx_第3頁
Spring in Action中文版(第二版)-Spring Security.docx_第4頁
Spring in Action中文版(第二版)-Spring Security.docx_第5頁
免費預(yù)覽已結(jié)束,剩余50頁可下載查看

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

你是否注意到在電視連續(xù)劇中大多數(shù)人是不鎖門的?這是司空見慣的。在情景喜劇宋飛正傳(Seinfeld)中,克雷默常常到杰麗的房間里從冰箱中拿東西吃。在老友經(jīng)(Friends)中,各種各樣的劇中人經(jīng)常不敲門就不加思索地進(jìn)入別人的房間。甚至有一次在倫敦,羅斯突然進(jìn)入錢德勒的旅館房間,差點兒撞見錢德勒和羅斯的妹妹的私情。在反斗小寶貝(Leave It to Beaver)熱播的年代,并不值得為人們不鎖門這一現(xiàn)象而大驚小怪。但是在如今這個隱私和安全極受重視的時代,看到電視劇中的角色允許他人大搖大擺地進(jìn)入自己的公寓房間或家中,實在讓人難以想像?,F(xiàn)實令人沮喪,有許多卑劣的人正在四處伺機(jī)偷走我們的金錢、財產(chǎn)、車輛和其他貴重物品。而隨著信息逐漸成為我們所擁有的最有價值的東西時,竊賊們偷偷進(jìn)入無保護(hù)的應(yīng)用程序設(shè)法來偷取我們的數(shù)據(jù)和身份信息,也就不足為奇。做為軟件開發(fā)人員,我們必須采取措施來保護(hù)我們應(yīng)用程序中的那些信息。無論你是通過用戶名/密碼來保護(hù)一個電子郵件賬號,還是基于交易個人身份號碼來保護(hù)一個經(jīng)紀(jì)賬戶,安全性都是絕大多數(shù)應(yīng)用程序的一個重要切面。本書作者是有意選擇“切面”這個詞來描述應(yīng)用程序的安全性。安全性是超越應(yīng)用程序功能特性的一個關(guān)注點。通常來說,應(yīng)用程序不應(yīng)該親自參與到對自己的保護(hù)中。盡管你可以把與安全相關(guān)的處理直接編碼到應(yīng)用程序中(這種情況并不少見),但是更好的做法還是將安全考慮與應(yīng)用考慮分開。聽上去好像安全性是通過“面向切面”技術(shù)實現(xiàn)的,其實還是如此。在本章中,我們將研究利用切面保護(hù)應(yīng)用程序的方法。不過,我們不必自己開發(fā)那些切面我們將著眼于Spring Security,這是一種基于Spring AOP和Servlet過濾器7的安全框架。7.1 Spring Security介紹Spring Security是一種為基于Spring的應(yīng)用程序提供說明性安全保護(hù)的安全框架。它提供全面的安全性解決方案,同時在Web請求級和方法調(diào)用級處理身份確認(rèn)和授權(quán)。在Spring Framework基礎(chǔ)上,Spring Security充分利用了依賴注入(DI,Dependency Injection)和面向切面技術(shù)。7.1.1 名稱中有什么在過去,Spring Security也被稱為Acegi Security(或者簡稱為Acegi)。Acegi長期以來一直是Spring的一個子項目。但是在本書作者寫到這兒時,正有計劃準(zhǔn)備將Acegi更加緊密地置于Spring項目傘之下。做為該行動的一部分,將不再使用Acegi這個名稱,而改用“Spring Security”。按照計劃,這一更改將在Acegi/Spring Security的1.1.0版本中開始實施。由于知道這一更改即將實施,因此本書作者決定先行一步,開始將Acegi稱為Spring Security,不過讀者還會在本章中看到一些該名稱。在保護(hù)Web應(yīng)用程序時,Spring Security使用Servlet過濾器來攔截Servlet請求,以實施身份認(rèn)證和執(zhí)行安全措施。并且,在第7.4.1節(jié)你將會看到,Spring Security采取了一種獨特的機(jī)制來聲明Servlet過濾器,使你可以使用Spring DI注入它所依賴的其他對象。Spring Security還可以通過保護(hù)方法調(diào)用在一個較低層的級別上執(zhí)行安全措施。在保護(hù)方法時,Spring Security使用Spring AOP來代理對象,將“切面”應(yīng)用于對象,以確保用戶只有在擁有恰當(dāng)授權(quán)時才能調(diào)用受保護(hù)的方法。無論你是只在Web請求級需要安全措施,還是需要較低層的方法安全措施,Spring Security都是使用如圖7.1所示的5個主要組件來實施安全措施的。圖7.1 Spring Security的基本要素在研究Spring Security安全機(jī)制的本質(zhì)之前,首先讓我們居高臨下地考察一下Spring Security以及每一個組件在保護(hù)應(yīng)用程序中所扮演的角色。 安全攔截器工作了一整天,當(dāng)你回到家時,需要打開家門上的鎖。而為了打開那個鎖,你必須先將一把鑰匙插到鎖孔中,并恰當(dāng)?shù)負(fù)軇渔i的制動栓,以打開彈簧鎖。如果鑰匙和鎖不匹配,就無法撥動制動栓,而彈簧鎖也就不會被打開。但是如果你有正確的鑰匙,那么所有的制動栓就都會接受這把鑰匙,彈簧鎖就會被打開,從而允許你把門打開。在Spring Security中,安全攔截器可以被看作是一把彈簧鎖,能夠阻止對應(yīng)用程序中受保護(hù)資源的訪問。為了彈開彈簧鎖,從而通過安全攔截器,你必須向系統(tǒng)提供“鑰匙”(通常是一對用戶名和密碼)。該“鑰匙”接著會嘗試撥開安全攔截器的“制動栓”,從而允許你訪問受保護(hù)的資源。安全攔截器的實際實施將取決于所要保護(hù)的資源。如果讀者正要在某個Web應(yīng)用程序中保護(hù)一個URL,那么相應(yīng)的安全攔截器將被當(dāng)做一個servlet過濾器來實施。但是如果你正要保護(hù)某個方法調(diào)用,那么切面將被用來加強安全性。讀者將會在本章稍后部分看到安全攔截器的這兩種形式。除了通過攔截對資源的訪問來加強安全性之外,安全攔截器幾乎無所事事。它并不實際應(yīng)用安全規(guī)則。相反,它把該職責(zé)委托給圖7.1底部所示的各種管理器。下面讓我們從認(rèn)證管理器開始,逐個看一下這些管理器。 認(rèn)證管理器第一道必須打開的安全攔截器的制動栓就是認(rèn)證管理器。認(rèn)證管理器負(fù)責(zé)辨認(rèn)你是誰。它是通過考慮你的主體(通常是一個用戶名)和你的憑證(通常是一個密碼)做到這點的。你的主體定義了你是誰,而你的憑證則是確認(rèn)你身份的證據(jù)。如果你的憑證足以使認(rèn)證管理器相信你的主體可以標(biāo)識你的身份,那么Spring Security就能知道它是在和誰打交道了。如同Spring Security的其余部分(以及Spring本身)一樣,認(rèn)證管理器也是一個基于接口的可插入組件。這使得它有可能與幾乎所有你能想象到的認(rèn)證機(jī)制一起使用Spring Security。在本章稍后你將看到,Spring Security帶有少數(shù)靈活的認(rèn)證管理器,它們包括絕大多數(shù)常見的認(rèn)證策略。 訪問決策管理器一旦Spring Security確定了你是誰,它就必須決定你是否對受保護(hù)的資源擁有訪問授權(quán)。訪問決策管理器是Spring Security鎖中第二道必須被打開的制動栓。訪問決策管理器執(zhí)行授權(quán),它考慮你的身份認(rèn)證信息和與受保護(hù)資源關(guān)聯(lián)的安全屬性來決定是否讓你進(jìn)入。舉例來說,安全規(guī)則也許規(guī)定只有主管才允許訪問某個受保護(hù)的資源。而如果你被授予了主管權(quán)限,那么第二道也是最后一道制動栓訪問決策管理器就會被打開,并且安全攔截器將會給你讓路,讓你取得對受保護(hù)資源的訪問權(quán)。就像認(rèn)證管理器一樣,訪問決策管理器也是可插入的。在本章稍后部分,我們將更進(jìn)一步研究Spring Security所帶的訪問決策管理器。 運行身份管理器如果你已經(jīng)通過了認(rèn)證管理器和訪問決策管理器,那么安全攔截器就會被開啟,門也就可以被打開了。但是在你轉(zhuǎn)動門把手進(jìn)去之前,安全攔截器可能還有一件事要做。即使你已經(jīng)通過身份認(rèn)證并且已經(jīng)獲得了訪問某一資源的授權(quán),門后也許還有更多的安全限制在等著你。舉例來說,你也許已被授權(quán)查看某一Web頁面,但是用于創(chuàng)建該頁面的對象可能有著與這一Web頁面本身不同的安全要求。運行身份管理器可以用來使用另一個身份替換你的身份,從而允許你訪問應(yīng)用程序內(nèi)部更深處的受保護(hù)對象。注意,并不是所有應(yīng)用程序都會需要身份替換。因此,運行身份管理器是一個可選的安全組件,在許多受Spring Security保護(hù)的應(yīng)用程序中并不需要運行身份管理器。 調(diào)用后管理器Spring Security的調(diào)用后管理器與其他安全管理器組件略有不同。其他安全管理器組件在受保護(hù)資源被訪問之前實施某種形式的安全措施強制執(zhí)行,而調(diào)用后管理器則是在受保護(hù)資源被訪問之后執(zhí)行安全措施。調(diào)用后管理器有點類似于在某些折扣商店和家用電器商店出口處等著檢查購物小票的人。他們這樣做的目的是確保你擁有從商店里搬走那些值錢物品的適當(dāng)權(quán)利。不過,調(diào)用后管理器是確保你被允許查看那些受保護(hù)資源返回的數(shù)據(jù),而不是確保你被允許從商店搬走大屏幕電視。如果調(diào)用后管理器建議一個服務(wù)層Bean,那么它便將有機(jī)會檢查從所建議方法返回的值。接下來,它可以決定當(dāng)前用戶是否被允許查看返回的對象。該調(diào)用后管理器還可以修改所返回的值,以確保當(dāng)前用戶只能夠訪問返回對象的特定屬性。與運行身份管理器類似,并不是所有應(yīng)用程序都會需要調(diào)用后管理器。你只會在你的應(yīng)用程序的安全方案要求訪問被限制在每個實例基礎(chǔ)上的定義域水平時需要調(diào)用后管理器?,F(xiàn)在,你已經(jīng)看到了Spring Security的全貌,我們就可以為RoadRantz應(yīng)用程序配置Spring Security了。對我們來說,我們將不需要運行身份管理器或調(diào)用后管理器,因此我們將把那兩個組件放到后面的高級Spring Security主題中。下面,讓我們首先從配置認(rèn)證管理器開始。7.2 驗證用戶身份在為應(yīng)用程序應(yīng)用安全措施時,決定是否允許用戶訪問受保護(hù)資源之前首先需要判斷用戶的身份。在絕大多數(shù)應(yīng)用程序中,這意味著為用戶顯示一個登錄界面,并要求他們提供用戶名和密碼。不同應(yīng)用程序提示用戶輸入其用戶名和密碼的方式有所不同。現(xiàn)在,我們假設(shè)用戶具體的登錄信息已經(jīng)提供,并且需要Spring Security驗證當(dāng)前用戶的身份。在本章稍后,我們將介紹以不同方式提示用戶輸入其用戶名和密碼的內(nèi)容。在Spring Security中,認(rèn)證管理器負(fù)責(zé)確定用戶的身份。而認(rèn)證管理器是由org.acegi- security.AuthenticationManager接口定義的:第三次一定會有好運啊哈!在AuthenticationManager的程序包名稱中有acegi這個單詞。正如本章較早前提及的那樣,Spring Security原先被稱為Acegi Security。在Acegi被正式更名為Spring Security時,其類的封裝方式也會更改。實際上,這將是Acegi/Spring Security已經(jīng)擁有的“第三壘”程序包名稱(譯者注:“第三壘”是指在菱形球場上由本壘逆時針數(shù)的第三個壘,是選手到本壘之前的最后一壘)。Acegi最初被封裝在net.sf.acegisecurity之下接著它被更改為org.acegisecurity。在1.1.0版發(fā)布時,它將很可能被重新封裝在org.springframework.security之下。盡管如此,由于那些更改尚未發(fā)生,因此本章中的示例仍顯示org.acegisecurity封裝方式。這里的authenticate()方法將會嘗試?yán)胦rg.acegisecurity.Authentication對象(它帶有相應(yīng)的主體和憑證)來驗證用戶身份。如果認(rèn)證成功,authenticate()方法會返回一個完整的Authentication對象,其中包括用戶已被授予的權(quán)限信息(它們將由認(rèn)證管理器使用)。如果認(rèn)證失敗,則它會拋出一個AuthenticationException。正如你所看到的,AuthenticationManager接口非常簡單,你可以相當(dāng)輕松地執(zhí)行自己的AuthenticationManager。但是Spring Security提供有ProviderManager,那是一個適用于絕大多數(shù)情形的AuthenticationManager執(zhí)行。所以,讓我們看一下如何使用ProviderManager,而不是討論開發(fā)自己的認(rèn)證管理器。7.2.1 配置ProviderManagerProviderManager是認(rèn)證管理器的一個實現(xiàn),它將驗證身份的責(zé)任委托給一個或多個認(rèn)證提供者,如圖7.2所示。圖7.2 ProviderManager將身份驗證的職責(zé)委托給一個或多個認(rèn)證提供者ProviderManager的用途是使你能夠根據(jù)多個身份管理源來認(rèn)證用戶。它不是依靠自己實現(xiàn)身份驗證,而是逐一認(rèn)證提供者的集合,直到某一個認(rèn)證提供者能夠成功地驗證該用戶的身份(或者是已經(jīng)嘗試完了該集合中所有的認(rèn)證提供者)。這使得Spring Security能夠為單個應(yīng)用程序提供多種認(rèn)證機(jī)制。下列XML程序塊顯示的是Spring配置文件中ProviderManager的一種典型配置:通過ProviderManager的providers屬性,可以為其提供認(rèn)證提供者列表。通常你只需要一個認(rèn)證提供者,但是在某些情況下,提供由若干個認(rèn)證提供者組成的列表會十分有用。在這種情況下,如果一個認(rèn)證提供者驗證身份失敗,可以嘗試另一個認(rèn)證提供者。Spring提供了若干個認(rèn)證提供者,如表7.1所列:表7.1 Spring Security提供的針對各種場合的認(rèn)證提供者認(rèn)證提供者(org.acegisecurity.*)目 的adapters.AuthByAdapterProvider使用容器適配器驗證身份。這使得驗證在Web容器(舉例來說,Tomcat、JBoss、Jetty、Resin等)內(nèi)所創(chuàng)建的用戶的身份成為可能。providers.anonymous.AnonymousAuthenticationProvider以匿名用戶方式驗證用戶。在即使用戶尚未登錄,仍需要用戶令牌時,會比較有用。續(xù)表認(rèn)證提供者(org.acegisecurity.*)目 的providers.cas.CasAuthenticationProvider根據(jù)JA-SIG“中心認(rèn)證服務(wù)”(CAS)驗證身份。在用戶需要單點登錄能力時會比較有用。providers.dao.DaoAuthenticationProvider從數(shù)據(jù)庫中獲取用戶信息,包括用戶名和密碼。providers.dao.LdapAuthenticationProvider根據(jù)某一輕量級目錄訪問協(xié)議(LDAP)服務(wù)器驗證身份。providers.jaas.JaasAuthenticationProvider從JAAS登錄配置中獲取用戶信息。providers.rememberme.RememberMeAuthenticationProvider驗證某一之前驗證過并且被記住的用戶的身份。這使得無需提示輸入用戶名和密碼即自動登錄某一用戶成為可能。providers.rcp.RemoteAuthenticationProvider根據(jù)遠(yuǎn)程服務(wù)驗證用戶身份。providers.TestingAuthenticationProvider用于單元測試。自動認(rèn)為一個TestingAuthenticationToken是有效的。不應(yīng)用于生產(chǎn)環(huán)境。providers.x509.X509AuthenticationProvider使用X.509證書驗證用戶身份。對于驗證實際上是其他應(yīng)用程序(比如,某一Web服務(wù)客戶端)的用戶身份會比較有用。runas.RunAsImplAuthenticationProvider針對身份已經(jīng)被運行身份管理器替換的用戶進(jìn)行認(rèn)證。正如你在表7.1中所看到的那樣,Spring Security幾乎為每一種需求都提供了一個認(rèn)證提供者。但是,如果仍未能找到符合你的應(yīng)用程序安全需求的認(rèn)證提供者,則可以隨時通過執(zhí)行viders.AuthenticationProvider接口來創(chuàng)建你自己的認(rèn)證提供者:也許讀者已經(jīng)注意到,這個AuthenticationProvider接口與前幾頁顯示的AuthenticationManager接口沒有什么太大不同,它們都有一個處理認(rèn)證的authenticate()方法。實際上,可以把認(rèn)證提供者看做是第二等級的認(rèn)證管理器。限于篇幅,本書將無法詳細(xì)介紹Spring Security的所有11個認(rèn)證提供者。不過,這里將集中介紹兩個最常用的認(rèn)證提供者,首先從DaoAuthenticationProvider開始,它支持進(jìn)行簡單的面向數(shù)據(jù)庫的身份驗證。7.2.2 根據(jù)數(shù)據(jù)庫驗證身份許多應(yīng)用程序?qū)ㄓ脩裘兔艽a在內(nèi)的用戶信息保存在關(guān)系數(shù)據(jù)庫中。如果那就是你的應(yīng)用程序保留用戶信息的方式,那么你會發(fā)現(xiàn),對于你的應(yīng)用程序來說,選擇Spring Security提供的DaoAuthenticationProvider可能會比較好。DaoAuthenticationProvider是一個簡單的認(rèn)證提供者,它使用數(shù)據(jù)存取對象(DAO)來從關(guān)系數(shù)據(jù)庫中檢索用戶信息(包括用戶的密碼)。在取得了所需的用戶名和密碼之后,DaoAuthenticationProvider通過比較從數(shù)據(jù)庫中檢索到的用戶名和密碼以及來自認(rèn)證管理器的通過Authentication對象中傳入的主體和憑證完成身份驗證(如圖7.3所示)。如果上述用戶名和密碼與主體和憑證相匹配,則用戶通過身份驗證,同時返回給認(rèn)證管理器一個已完全填充好的Authentication對象。否則會拋出一個AuthenticationException,表明身份驗證失敗。圖7.3 DaoAuthenticationProvider通過從數(shù)據(jù)庫中獲取用戶信息幫助認(rèn)證管理器進(jìn)行身份驗證配置一個DaoAuthenticationProvider再簡單不過了。下面這一段XML摘選顯示了如何聲明一個DaoAuthenticationProvider Bean,并且裝配上它所依賴的DAO:這里的userDetailsService屬性被用來指定將用于從數(shù)據(jù)庫中檢索用戶信息的那個Bean。這個屬性期望org.acegisecurity.userdetails.UserDetailsService的一個實例。剩下來的問題就是那個userDetailsService Bean是如何配置的了。這里的UserDetailsService接口要求只執(zhí)行一個方法:這個方法相當(dāng)顯而易見,而且讀者可能已經(jīng)在思考幾種可以實現(xiàn)這個接口的方法了。但是在你開始自行編寫UserDetailsService的實現(xiàn)之前,你可能有興趣知道Spring Security帶有兩個現(xiàn)成的可供選擇的AuthenticationDao實現(xiàn):InMemoryDaoImpl和JdbcDaoImpl。下面讓我們來看一看這兩個類是如何查找用戶詳細(xì)信息的,首先從InMemoryDaoImpl開始。 使用內(nèi)存DAO盡管假定AuthenticationDao對象將總是通過查詢關(guān)系數(shù)據(jù)庫來獲取用戶信息,可能看上去是一種很自然的想法,但實際上并不是必須要那樣的。如果你的應(yīng)用程序的身份驗證需求是微不足道的,或者是為了開發(fā)期間的方便起見,也許更簡單的做法是直接在Spring配置文件中配置你的用戶信息。為此,Spring Security提供了InMemoryDaoImpl,那是一個從Spring配置文件中獲取用戶信息的UserDetailsService實現(xiàn)。這里是你可能如何在Spring配置文件中配置InMemoryDaoImpl的一個示例:這里的userMap屬性通過一個org.acegisecurity.userdetails.memory.UserMap對象來定義一組用戶名、密碼和權(quán)限。幸運的是,在裝配InMemoryDaoImpl時,你不必為構(gòu)建一個UserMap實例而操心,因為Spring Security提供有一個屬性編輯器,它能夠幫你把一個String轉(zhuǎn)化為一個UserMap對象。在這個userMap的每一行上,String都是一個名字-值對,其中名字就是用戶名,值是一個由逗號分隔的列表,它以相應(yīng)用戶的密碼開頭,后面跟著一個或多個準(zhǔn)備授予該用戶的權(quán)限的名稱。圖7.4分解開了上述用戶映像中的一個條目格式。圖7.4 Spring Security用戶映像中一個用戶名對應(yīng)一個密碼、授予的權(quán)限以及它們的狀態(tài)(可選)在前面的authenticationDao Bean聲明中,定義了四個用戶:palmerd、bauerj、obrianc和myersn。他們的密碼分別是4moreyears、ineedsleep、nosmile和traitor。各用戶的權(quán)限授予情況如下:n 用戶名為palmerd的用戶已被給予ROLE_PRESIDENT權(quán)限;n bauerj被給予ROLE_FIELD_OPS;n myersn被給予ROLE_CENTRAL_OPS;n obrianc用戶被授予兩個權(quán)限:ROLE_SR_ANALYST和ROLE_OPS。需要特別注意這里的palmerd和myersn。在他們的密碼之后,緊接著一個特殊的disabled標(biāo)志,表明他們已經(jīng)被禁用(因此無法驗證身份)。盡管InMemoryDaoImpl既方便又簡單,但是它有一些顯而易見的局限性。主要是,對安全性進(jìn)行管理時要求你編輯Spring配置文件并且重新部署應(yīng)用。雖然在開發(fā)環(huán)境下這是可以接受的(而且可能還是有幫助的),但是對于生產(chǎn)用途而言這種做法就太笨拙了。因此,本書作者強烈反對在生產(chǎn)環(huán)境下使用InMemoryDaoImpl。相反,你應(yīng)該考慮使用我們接下來將介紹的JdbcDaoImpl。 聲明一個JDBC DAOJdbcDaoImpl是一個簡單而靈活的認(rèn)證DAO,它從關(guān)系數(shù)據(jù)庫中檢索用戶的信息。在它最簡單的形式中,只需要一個javax.sql.DataSource對象的引用,而且它可以通過以下方式在Spring配置文件中進(jìn)行聲明:正如這里配置的那樣,JdbcDaoImpl對用戶信息在相應(yīng)數(shù)據(jù)庫中的存儲情況做了一些基本的假設(shè)。特別是,它假設(shè)有一張“Users”表和一張“Authorities”表,如圖7.5所示。當(dāng)JdbcDaoImpl查找用戶信息時,它會使用下列SQL作為查詢語句:類似地,當(dāng)查找某個用戶的授權(quán)時,JdbcDaoImpl會使用下列SQL:盡管JdbcDaoImpl假定的表結(jié)構(gòu)非常直接,它們很可能與你已經(jīng)為自己的應(yīng)用程序安全建立的表結(jié)構(gòu)不一致。比如,在RoadRantz應(yīng)用程序中,Motorist表保存已注冊用戶的用戶名(在email列中)和密碼。這是否意味著我們無法在RoadRantz應(yīng)用程序中使用JdbcDaoImpl來驗證駕車者的身份呢?當(dāng)然不是。但是如果準(zhǔn)備使用JdbcDaoImpl,我們就必須費點力氣通過設(shè)置usersBy- UserNameQuery屬性來告訴它如何找到用戶信息。以下對authenticationDao Bean的調(diào)整將使得它能夠從RoadRantz的Motorist表中查詢用戶:現(xiàn)在,JdbcDaoImpl已經(jīng)知道到Motorist表中查找認(rèn)證信息了。但是,我們還必須告訴JdbcDaoImpl如何在相應(yīng)數(shù)據(jù)庫中查詢某一用戶的授權(quán)。為此,我們將設(shè)置authoritiesByUsernameQuery屬性:到這里,我們已經(jīng)配置好JdbcDaoImpl從Motorist_Privileges表中檢索駕車者的授權(quán)。這里的查詢還加入了Motorist表,那是因為Motorist_Privileges表只是通過一個外關(guān)鍵字來了解某個Motorist的,而JdbcDaoImpl期望這個查詢能通過用戶名檢索權(quán)限。 使用加密的密碼當(dāng)DaoAuthenticationProvider將用戶在身份驗證時提供的密碼與從數(shù)據(jù)庫中檢索到密碼相比較時,它假設(shè)一直存儲著的那個密碼是沒有加密的。為了增強安全性,你可能會希望在將那個密碼存儲到數(shù)據(jù)庫中之前對它進(jìn)行加密。但是,如果那個密碼以加密形式存儲在數(shù)據(jù)庫中,那么用戶提供的密碼必須也要加密,只有這樣,這兩個密碼才能相互比較。為了適應(yīng)加密的密碼,DaoAuthenticationProvider可以裝配一個密碼編碼器。Spring Security帶有幾個密碼編碼器可供選擇,如表7.2所示。表7.2 Spring Security的密碼編碼器密碼編碼器(viders.*)目 的encoding.Md5PasswordEncoder在密碼上執(zhí)行“信息摘要”(MD5)編碼encoding.PlaintextPasswordEncoder在密碼上不執(zhí)行任何編碼,照原樣返回它encoding.ShaPasswordEncoder在密碼上執(zhí)行“安全散列算法”(SHA)編碼ldap.authenticator.LdapShaPasswordEncoder使用LDAP SHA和salted-SHA(SSHA)編碼技術(shù)編碼密碼在默認(rèn)情況下,DaoAuthenticationProvider使用PlaintextPasswordEncoder,那意味著密碼仍然沒有被編碼。但是,我們可以通過設(shè)置DaoAuthenticationProvider的passwordEncoder屬性來指定另一種編碼方法。舉例來說,要讓DaoAuthenticationProvider使用MD5編碼技術(shù),可以加入以下代碼:你還將需要為編碼器設(shè)置一個種子源(salt source)。一個種子源為所用的編碼技術(shù)提供種子(salt),或者稱加密密鑰。Spring Security提供有兩個種子源:n SystemWideSaltSource對所有用戶提供相同的種子;n ReflectionSaltSource利用用戶的User對象中某個指定屬性的反射來生成種子。在這兩個種子源中,ReflectionSaltSource更加安全,因為每一個用戶的密碼很可能會使用不同的種子值來編碼。即使假設(shè)有黑客推測出了用來編碼某個用戶密碼的種子,他們也不太可能使用同一個種子破解開另一個用戶的密碼。要想使用一個ReflectionSaltSource,可以通過如下方式將它裝配到DaoAuthenticationProvider的saltSource屬性中:在這里,用戶的userName屬性被用作種子來加密用戶的密碼。要特別值得重視的是,必須保證該種子是靜態(tài)的,而且永遠(yuǎn)不會改變;否則,就再也不可能對這個用戶身份進(jìn)行驗證了(除非是在使用新的種子更改之后,那個密碼又被重新編碼回來了)。盡管ReflectionSaltSource的確是更加安全,但是SystemWideSaltSource更加簡單,而且對于絕大多數(shù)情況來說已經(jīng)足夠。SystemWideSaltSource使用單個種子值加密所有用戶的密碼。要想使用一個SystemWideSaltSource,可以通過如下方式裝配saltSource屬性:在這里,相同的種子值A(chǔ)BC123XYZ789被用于加密所有密碼。 緩存用戶信息每次當(dāng)請求一個受保護(hù)的資源時,認(rèn)證管理器就被調(diào)用以檢索用戶的安全信息。但是如果檢索用戶信息涉及到要查詢數(shù)據(jù)庫,那么每次都查詢相同的數(shù)據(jù)就可能會妨礙應(yīng)用性能。考慮到用戶的信息一般不會頻繁改變,因此也許更好的做法是根據(jù)第一次查詢緩存那些用戶數(shù)據(jù),然后在后續(xù)的每次查詢中從緩存中檢索用戶信息。為了啟用用戶信息的緩存功能,我們必須給DaoAuthenticationProvider提供viders.dao.UserCache接口的一個實現(xiàn)。這個接口批準(zhǔn)三個方法的執(zhí)行:UserCache中的這幾個方法都無需加以說明,它們提供向緩存中放入、檢索或刪除用戶明細(xì)信息的功能。這樣以來,編寫你自己的UserCache實現(xiàn)就應(yīng)該相當(dāng)簡單了。然而,在你考慮開發(fā)自己的UserCache實現(xiàn)之前,應(yīng)該首先考慮Spring Security提供的兩個方便的實現(xiàn):n viders.dao.cache.NullUserCachen viders.dao.cache.EhCacheBasedUserCache事實上,NullUserCache并不執(zhí)行任何緩存行為。相反,它每次都是從它的getUserFromCache方法返回null,促使DaoAuthenticationProvider來查詢用戶信息。這是DaoAuthenticationProvider使用的默認(rèn)UserCache實現(xiàn)。EhCacheBasedUserCache是一個更加有用的緩存實現(xiàn)。顧名思義,它是基于EHCache實現(xiàn)的。和DaoAuthenticationProvider一起使用EHCache是很簡單的,只需要將一個EhCacheBasedUserCase Bean裝配進(jìn)DaoAuthenticationProvider的userCache屬性即可:這里的cache屬性引用一個ehcache Bean,后者應(yīng)該是一個EHCache Cache對象。得到那樣一個Cache對象的一種途徑是使用Spring Modules的緩存模塊。舉例來說,下面這段XML就使用Spring Modules來配置EHCache:讀者可能還記得本書第5章的內(nèi)容,Spring Modules的EhCacheFactoryBean是一種Spring Factory Bean,它生成一個EHCache Cache對象。實際的緩存配置是在ehcache.xml文件中找到的,它將會從類途徑中檢索到。當(dāng)你的應(yīng)用程序的安全信息保存在某一關(guān)系數(shù)據(jù)庫中時,DaoAuthenticationProvider將非常棒。不過,一個應(yīng)用程序的安全性經(jīng)常構(gòu)建成根據(jù)一個LDAP服務(wù)器進(jìn)行身份驗證。下面讓我們來看一下如何使用Spring Security的LdapAuthenticationProvider,在必須通過LDAP進(jìn)行身份驗證時,它是更加適用的一種選擇。7.2.3 根據(jù)LDAP倉庫進(jìn)行身份驗證Spring Security支持通過LdapAuthenticationProvider根據(jù)LDAP進(jìn)行身份驗證,LdapAuthenticationProvider是一個知道如何根據(jù)LDAP倉庫查看用戶憑證的認(rèn)證提供者。下列舉例說明的是針對LdapAuthenticationProvider的一種典型配置:正如讀者可以看到的那樣,LdapAuthenticationProvider并沒有太多令人興奮的地方,那兒沒有有關(guān)如何找到相應(yīng)LDAP服務(wù)器或倉庫初始上下文的具體信息。相反,LdapAuthenticationProvider通過構(gòu)造函數(shù)注入,與一個authenticator和一個populator裝配在一起。那些Bean是什么,它們是用來干什么的?事實上,盡管LdapAuthenticationProvider聲稱知道如何與一個LDAP倉庫對話,但它實際上依賴兩個策略對象來做真正的工作:n authenticator策略根據(jù)LDAP倉庫處理實際的身份驗證(舉例來說,驗證用戶憑證)。Authenticator策略可以是任何實現(xiàn)viders.ldap.Ldap Authenticator的對象。n populator策略負(fù)責(zé)從LDAP倉庫檢索用戶獲得的權(quán)限集。Populator策略是任何實現(xiàn)viders.ldap.LdapAuthoritiesPopulator的對象。由于認(rèn)證和權(quán)限責(zé)任被定義為策略,從LdapAuthenticationProvider分離出來了,你就能夠在最適合你的應(yīng)用程序安全需求的策略實現(xiàn)中進(jìn)行裝配了。因此,剩下的問題就是這authenticator和populator Bean是如何定義的了。下面讓我們考察authenticator Bean開始,它為LdapAuthenticationProvider定義認(rèn)證策略。 利用LDAP綁定進(jìn)行身份驗證在要根據(jù)LDAP進(jìn)行身份驗證時,通常會采取以下兩種方法:n 利用一個LDAP用戶的用戶名和密碼綁定到LDAP服務(wù)器;n 在LDAP中檢索一個用戶的條目,然后將提供的密碼和檢索到的LDAP記錄中的密碼屬性相比較。為了綁定身份驗證,Spring Security帶有一個稱為BindAuthenticator的LdapAuthenticator實現(xiàn)。BindAuthenticator使用一個LDAP bind運算符來把一個用戶綁定到LDAP服務(wù)器。這種方法依靠LDAP服務(wù)器來驗證所綁定用戶的憑證。下列在Spring中聲明一個BindAuthenticator:在這里,我們已經(jīng)聲明這個BindAuthenticator是通過一個構(gòu)造函數(shù)參數(shù)和通過userDnPatterns屬性來注入的。稍后我們將回到這個構(gòu)造函數(shù)參數(shù)。首先,讓我們考慮那個userDnPatterns屬性。這個userDnPatterns屬性是用來告訴BindAuthenticator如何在LDAP中找到某個用戶的。它的值是一個模式列表,是BindAuthenticator將用做識別名(DN)來識別用戶的一個或多個模式。在這里,我們只使用一個DN模式,如圖7.6所示。DN模式中的0是一個充當(dāng)用戶名占位符的模式參數(shù)。舉例來說,如果用戶名為cwagon,那么用來綁定到LDAP的DN將是uid=cwagon,ou=motorists。現(xiàn)在,回到前面那個構(gòu)造函數(shù)參數(shù)。為了能夠做好自己的工作,BindAuthenticator需要知道的主要事情是如何訪問相應(yīng)的LDAP倉庫。因此,它和一個構(gòu)造函數(shù)參數(shù)一同構(gòu)建,裝配到initialDirContextFactory,其聲明方式如下:DefaultInitialDirContextFactory捕獲連接到一臺LDAP服務(wù)器所需的全部信息,并生成一個JNDI DirContext對象。如果讀者不太了解JNDI或DirContext,則不必?fù)?dān)心這些具體細(xì)節(jié),只需記住BindAuthenticator是利用DefaultInitialDirContextFactory來獲悉如何到達(dá)相應(yīng)的LDAP倉庫即可。用來創(chuàng)建DefaultInitialDirContextFactory的那個構(gòu)造函數(shù)參數(shù)和LDAP提供者的URL一起裝配。在這里,我們已經(jīng)把它和對RoadRantz LDAP服務(wù)器8的一個引用裝配在一起,并且在dc=roadrantz,dc=com處建立了初始上下文。用來查找用戶信息的DN將被關(guān)系到這個初始上下文。 通過比較密碼進(jìn)行身份驗證做為綁定認(rèn)證之外的一種可選方式,Spring Security還支持通過使用PasswordComp- arisonAuthenticator的密碼比較進(jìn)行身份驗證。PasswordComparisonAuthenticator通過比較提供的密碼和用戶記錄中的一個密碼屬性(在默認(rèn)情況下是userPassword)進(jìn)行工作。它在Spring中的配置可能如下所示:注意,除了類名稱外,這個PasswordComparisonAuthenticator聲明與前面的BindAuth- enticator聲明完全相同。那是因為在它們的最簡單格式中,兩者在根本上是相同的。它們都需要一個初始化上下文工廠來知道如何到達(dá)相應(yīng)的LDAP倉庫,而且都需要一個或多個DN模式來定位用戶記錄。但是,還有一些屬性可以用來自定義PasswordComparisonAuthenticator。舉例來說,如果默認(rèn)的userPassword屬性并不符合你的需求,則可以通過裝配一個新的值到passwordAttributeName屬性來覆蓋它。比如說,按如下方式聲明PasswordComparison- Authenticator,以對照名為userCredentials的屬性比較所提供的密碼:你可能選擇的另一個自定義內(nèi)容是密碼如何在LDAP中編碼。在默認(rèn)情況下,PasswordComparisonAuthenticator使用Spring Security的LdapShaPasswordEncoder在比較之前編碼密碼。LdapShaPasswordEncoder支持LDAP“安全散列算法”(SHA)和SSHA(salted-SHA)編碼技術(shù)。但是如果這些不符合你的需求,任何一個org.acegisecurity. providers.encoding.PasswordEncoder實現(xiàn)(包括表7.2中的那些)都可以被裝配進(jìn)passwordEncoder屬性中。舉例來說,如果需要在LDAP中以純文本方式存儲密碼(不建議這樣,但是可能有這樣的情況),那么可以像下面這樣聲明PasswordComparisonAuthenticator:在轉(zhuǎn)而研究populator策略Bean之前,讓我們再最后調(diào)整一下initialDirContextFactory bean。與BindAuthenticator不同,PasswordComparisonAuthenticator并不利用用戶的DN綁定到LDAP。有些LDAP提供者允許匿名綁定,在那種情況下,initialDirContextFactory將會照常工作。不過,出于安全考慮,絕大多數(shù)LDAP提供者并不允許匿名綁定,因此我們將需要為DefaultInitialDirContextFactory提供一個管理器DN和密碼來綁定:當(dāng)DefaultInitialDirContextFactory訪問LDAP時,它會綁定為那個管理器,并且在比較用戶的密碼時代表該用戶?,F(xiàn)在,讓我們來配置populator策略Bean,以完成整個LDAP身份驗證內(nèi)容。 聲明populator策略Bean驗證用戶身份只是LdapAuthenticationProvider執(zhí)行的第一步。一旦用戶的身份被確定,LdapAuthenticationProvider必須檢索該用戶被授予的權(quán)限列表,以確定該用戶在當(dāng)前應(yīng)用程序中具有哪些權(quán)力。如同身份驗證的情況一樣,LdapAuthenticatorProvider使用一個策略對象來從LDAP查找用戶被授予的權(quán)限。Spring Security帶有一個LdapAuthoritiesPopulator接口的實現(xiàn):DefaultLdapAuthoritiesPopulator。下面是DefaultLdapAuthoritiesPopulator在Spring中的配置情況:讀者首先會注意到的是,DefaultLdapAuthoritiesPopulator的構(gòu)建中包含兩個構(gòu)造函數(shù)參數(shù),其中第一個是對我們的老朋友initialDirContextFactory的一個引用。幾乎和authenticator策略Bean一樣,populator策略Bean需要知道如何到達(dá)相應(yīng)的LDAP倉庫,以檢索用戶被授予的權(quán)限。第二個構(gòu)造函數(shù)參數(shù)幫助DefaultLdapAuthoritiesPopulator在LDAP倉庫中查找組。由于LDAP倉庫實際上是分層的,安全組隨處可見。這個構(gòu)造函數(shù)參數(shù)指定一個基礎(chǔ)DN,根據(jù)它來搜索組。這個基礎(chǔ)DN與初始上下文相關(guān)。因此,對于組基礎(chǔ)DN為“ou=groups”的情況,我們將搜索“ou=groups,dc=roadrantz,dc=com”中的組。最后,這里的groupRoleAttribute屬性指定將包含角色信息(它們實際上能被轉(zhuǎn)化成用戶被授予的權(quán)限)的特征名稱。它的默認(rèn)值為cn,但是在這個示例中,我們把它設(shè)置成了ou。這樣配置之后,DefaultLdapAuthoritiesPopulator就會檢索到所有包含指定用戶的組更確切地說,應(yīng)該是所有member屬性為該用戶DN的組。舉例來說,假設(shè)你有一個LDAP倉庫,填充有下列LDIF:9當(dāng)名為craig的用戶通過身份驗證時,他獲得的權(quán)限將包括ROLE_MOTORIST和ROLE_VIP。但是,當(dāng)raymie通過身份驗證時,她獲得的權(quán)限將只包括ROLE_MOTORIST,因為vips組的member屬性中并沒有她的DN。注意,這里的組名稱(在ou屬性中)被變換成大寫字母,然后再加上前綴ROLE_。這里將大小寫歸一化,只是為了方便幫助找到用戶的權(quán)限,而不必管它是小寫的還是大寫的。你可以通過把convertToUpperCase屬性設(shè)置為false來關(guān)閉這一行為。這里的ROLE_前綴是為了RoleVoter而提供的,我們將在7.3.2節(jié)中討論它。如果你更希望使用另一個不同的角色前綴,則可以根據(jù)自己的喜好任意配置DefaultLdap- AuthoritiesPopulator的rolePrefix屬性。舉例來說,要想關(guān)閉大寫字母歸一化,并且將角色前綴更改為GROUP_,則可以像下面那樣配置DefaultLdapAuthoritiesPopulator:你可能希望對DefaultLdapAuthoritiesPopulator做的再一個調(diào)整就是更改它查找成員的方式。一般來說,它查找member屬性含有所需用戶DN的組。如果你的LDAP被設(shè)置成如此使用member屬性,那么一切OK。但是,讓我們假設(shè)你的LDAP倉庫使用一個associate屬性(而不是member屬性)跟蹤成員資格。在那種情況下,你將希望像下面那樣設(shè)置DefaultLdapAuthoritiesPopulator的groupSearchFilter屬性:請注意,這里的groupSearchFilter屬性使用0模式參數(shù)表示用戶的DN。現(xiàn)在,我們已經(jīng)學(xué)會了如何使用Spring Security的身份驗證處理Bean來識別用戶。接下來,讓我們看一下Spring Security是如何確定一個已通過驗證的用戶是否擁有訪問受保護(hù)資源的適當(dāng)權(quán)限的。7.3 控制訪問身份驗證只是Spring Security安全保護(hù)機(jī)制中的第一步。一旦Spring Security弄清用戶的身份后,它必須決定是否允許用戶訪問由它保護(hù)的資源。在圖7.1中,我們已經(jīng)配置了認(rèn)證管

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論