本文檔是"Spring框架下Acegi安全系統(tǒng)"(Acegi Security System for Spring)的一份參考指南,Acegi安全系統(tǒng)是由一序列類(lèi)構(gòu)成,這些類(lèi)為Spring框架提供認(rèn)證和授權(quán)服務(wù)。
我得感謝這份參考在Spring框架中用DocBook配置進(jìn)行了精心的包裝。Spring小組在他們的DocBook中對(duì)Chris Bauer (Hibernate)的幫助表示感謝。
第一章 安全
1.1 準(zhǔn)備
為了安全使用Acegi,Acegi的每一個(gè)正式發(fā)布的JAR都署上了一個(gè)項(xiàng)目前導(dǎo)字符.這不表示對(duì)受權(quán)書(shū)中的免責(zé)聲明有任何的改變,但它確保你能使用一個(gè)適當(dāng)?shù)慕?jīng)過(guò)檢查的正式構(gòu)建的Acegi安全系統(tǒng).有關(guān)怎樣驗(yàn)證JAR文件是否正確地簽署以及哪些證書(shū)用來(lái)對(duì)他們進(jìn)行認(rèn)證的說(shuō)明請(qǐng)參考在發(fā)布版本中根目錄下的readme.txt文件.
1.2介紹
Acegi通過(guò)對(duì)流行的WEB容器的可選集成而為使用Spring編寫(xiě)的項(xiàng)目提供認(rèn)證與授權(quán)的能力.這種安全架構(gòu)是全部用"Spring方式"開(kāi)發(fā),包括使用bean contexts、攔截器和接口驅(qū)動(dòng)的編程方式。結(jié)果,對(duì)那些為基于Spring應(yīng)用的尋求安全保證應(yīng)用來(lái)說(shuō),Acegi是一個(gè)非常有用的外置式的安全架構(gòu),它很容易適用于各種復(fù)機(jī)的用戶(hù)化需求。
安全系統(tǒng)保括兩個(gè)顯而易見(jiàn)的方面:認(rèn)證和授權(quán)。前者解決調(diào)用者是否是他們所聲稱(chēng)的人的問(wèn)題,而授權(quán)則是關(guān)于已經(jīng)經(jīng)過(guò)認(rèn)證的調(diào)用者是否允許進(jìn)行給定操作的問(wèn)題。
在Acegi中,需要認(rèn)證的用戶(hù)、系統(tǒng)或都代理都作為一個(gè)"參與者"來(lái)題及。這個(gè)安全架構(gòu)沒(méi)有你在其它安全系統(tǒng)是所熟悉的角色或組的概念,即使Acegi完全提供相應(yīng)的功能。
1.2.1 發(fā)布號(hào)
理解Acegi的發(fā)布號(hào)的含意是有用的,因?yàn)樗鼘⒂兄谧C實(shí)你已經(jīng)(或者還沒(méi)將它)升級(jí)到這個(gè)項(xiàng)目的未來(lái)版本的。正式地,我們使用"Apache Portable Runtime Project"版本號(hào)使作指南,你可以在http://apr.apache.org/versioning.html
中看到。為方便起見(jiàn),我們引用了那頁(yè)中的介紹。
"版本號(hào)使用標(biāo)準(zhǔn)的三個(gè)一組的整數(shù)來(lái)表示:主版本號(hào).次版本號(hào).補(bǔ)丁號(hào)。主版本號(hào)表示API不兼容和對(duì)它進(jìn)行了大型升級(jí)。次版本號(hào)表示與之前的舊的較小的版本在源代碼和二進(jìn)制代碼上保持兼容性。補(bǔ)丁級(jí)版本號(hào)的改變表示無(wú)論向前向后完全兼容。
1.3 系統(tǒng)設(shè)計(jì)
1.3.1 關(guān)鍵組件
大多數(shù)企業(yè)級(jí)的應(yīng)用有四個(gè)基本安全需求。首先,他們要能夠認(rèn)證一個(gè)訪問(wèn)者;其次,他們需要有能力對(duì)web請(qǐng)求提供安全保證;第三,企業(yè)級(jí)應(yīng)用需要有能力對(duì)服務(wù)層方法提供安全保證;最后,企業(yè)級(jí)應(yīng)用經(jīng)常要有能力對(duì)域?qū)ο髮?shí)例提供安全保護(hù)。Acegi提供了一個(gè)通用的框架,來(lái)滿(mǎn)足所有這四個(gè)通用的企業(yè)級(jí)應(yīng)用安全需求。
Acegi基本上由八個(gè)關(guān)鍵功能部分組成。
Authentication對(duì)象 |
它包括參與者、認(rèn)證證書(shū)和賦予參與者的權(quán)限。這個(gè)對(duì)象也能存儲(chǔ)一些與認(rèn)證請(qǐng)求有關(guān)的附加信息。例如源TCP/IP地址. |
SecurityContextHolder對(duì)象 |
它包含一個(gè)在線程級(jí)對(duì)象內(nèi)的Authentication 認(rèn)證對(duì)象 |
AuthenticationManager對(duì)象 |
它鑒別由ContextHolder引進(jìn)的Authentication對(duì)象. |
AccessDecisionManager對(duì)象 |
授權(quán)一個(gè)給定的操作。 |
RunAsManager對(duì)象 |
當(dāng)一個(gè)給定的操作被執(zhí)行時(shí),任意替代Authentication 對(duì)象 |
"安全對(duì)象"攔截器 |
配合認(rèn)證、授權(quán)、run-as替換,從而調(diào)用處理執(zhí)行給定的操作。 |
AfterInvocationManager對(duì)象 |
能夠修改一個(gè)從"安全對(duì)象"調(diào)用中返的對(duì)象。例如刪去一些參與者沒(méi)有授權(quán)存取的集合元素。 |
存取控制列表(ACL)管理包 |
用于獲取應(yīng)用于域?qū)ο髮?shí)例ACLs。 |
"安全對(duì)象"攔截器執(zhí)行Acegi安全系統(tǒng)中的大多數(shù)關(guān)鍵類(lèi)。這樣做是為了利用框架的主要特性。
由于它的重要性,圖1表示了AbstractSecurityInterceptor 攔截器類(lèi)的主要關(guān)系和具體實(shí)現(xiàn)。
500)this.width=500" align=absMiddle border=0>
每個(gè)"安全對(duì)象"攔截器(下文中稱(chēng)為"保護(hù)性攔截器")作用于一個(gè)特殊類(lèi)型的"安全對(duì)象"。那么,什么是安全對(duì)象?安全對(duì)象是指對(duì)它采取了安全保護(hù)措施的任何類(lèi)型的對(duì)象。一個(gè)安全對(duì)象必須提供某種形式回調(diào),這樣當(dāng)需要的時(shí)候保護(hù)性攔截器就能明顯地起作用。保護(hù)性攔截器回調(diào)對(duì)象同時(shí)繼續(xù)所請(qǐng)求的操作。如果安全對(duì)象不能提供一個(gè)本地的回調(diào)方法,就要寫(xiě)一個(gè)包裝器來(lái)實(shí)現(xiàn)它。
每個(gè)安全對(duì)象都有一個(gè)實(shí)現(xiàn)它的包放在org.acegisecurity.intercept包中。在安全系統(tǒng)中的每個(gè)其它包是一個(gè)獨(dú)立的安全對(duì)象.因此它能支持上述的任何一種安全對(duì)象.
只有那些想對(duì)攔截和認(rèn)證請(qǐng)求采用全新方法的開(kāi)發(fā)者才需要直接使用安全對(duì)象.例如有可能建立一個(gè)新的安全對(duì)象來(lái)對(duì)沒(méi)有使用 MethodInvocations 對(duì)象的消息系統(tǒng)提供安全保護(hù).大多數(shù)的Spring應(yīng)用將簡(jiǎn)單地完全透明地使用三種普遍支持安全對(duì)象類(lèi)型 ((AOP Alliance MethodInvocation, AspectJ 連接點(diǎn) 和 web 請(qǐng)求過(guò)濾攔截器)
Acegi安全系統(tǒng)中八個(gè)關(guān)鍵部分的每個(gè)部分都將在本文中詳細(xì)討論.
1.3.2 所支持的安全對(duì)象
如圖1所示,目前Acegi安全系統(tǒng)支持三種安全對(duì)象.
第一種對(duì)象處理AOP Alliance MethodInvocation.這種安全對(duì)象用于保護(hù)Spring Bean.開(kāi)發(fā)者通常用它來(lái)保護(hù)他們的業(yè)務(wù)對(duì)象.為了生成一個(gè)標(biāo)準(zhǔn)的Spring-hosted類(lèi)型的bean 用作MethodInvocation,Bean簡(jiǎn)單地通過(guò) ProxyFactoryBean 或者 BeanNameAutoProxyCreator 或者 DefaultAdvisorAutoProxyCreator來(lái)公布.大多數(shù)Spring開(kāi)發(fā)者對(duì)此都已很熟悉,因?yàn)樗麄兪褂眠^(guò)Spring的事務(wù)處理和Spring的其它方面.
第二種對(duì)象是AspectJ連接點(diǎn)對(duì)象.AspectJ在域?qū)ο髮?shí)例安全方面有一種特殊的應(yīng)用,這通常是在 Spring bean容器之外.通過(guò)使用AspectJ,利用標(biāo)準(zhǔn)的構(gòu)造函數(shù),如new Person(),Acegi 安全系統(tǒng)對(duì)他們提供安全保護(hù). AspectJSecurityInterceptor仍然由Spring管理.它創(chuàng)建Aspect單例,并通過(guò)適當(dāng)?shù)恼J(rèn)證管理器、存取判別管理器等將它聯(lián)系起來(lái)。
第三種是過(guò)濾器調(diào)用(FilterInvocation)對(duì)象.它包含在Acegi安全系統(tǒng)內(nèi)。它由一個(gè)包含的過(guò)濾器創(chuàng)建并簡(jiǎn)單地封裝了HTTP ServletRequest、ServletResponse和FilterChain 。過(guò)濾器調(diào)用對(duì)象對(duì)HTTP資源提供了安全保護(hù)。開(kāi)發(fā)者通常不需要了解它的工作機(jī)制,因?yàn)樗麄冎灰谒麄兊膚eb.xml中添加過(guò)濾器來(lái)讓安全系統(tǒng)工作。
1.3.3配置屬性
每一個(gè)安全對(duì)象都能作用于一些特殊的請(qǐng)求。例如,MethodInvocation能用于對(duì)帶有任意參數(shù)的任何方的的調(diào)用。而FilterInvocation能用在任何的HTTP URL上。
Acegi 安全系統(tǒng)需要記錄一些配置信息,這些配置信息用于各種可能的請(qǐng)求。例如BankManager.getBalance (int accountNumber)請(qǐng)求所需的安全配置與BankManager.approveLoan (int applicationNumber)請(qǐng)求所需的安全配置有很大的不同。與此類(lèi)似,http://some.bank.com/index.htm
請(qǐng)求所需的安全配置與http://some.bank.com/manage/timesheet.jsp
請(qǐng)求所需的安全配置也有很大的不同。
為了存儲(chǔ)針對(duì)各種不同請(qǐng)求的各種安全配置,就要使用配置屬性。在實(shí)現(xiàn)上配置屬性通過(guò)ConfigAttribute接口來(lái)表示。SecurityConfig是ConfigAttribute接口的一個(gè)具體實(shí)現(xiàn),它簡(jiǎn)單地把配置屬性當(dāng)作一個(gè)串(String)來(lái)存儲(chǔ)。
與特定請(qǐng)求相聯(lián)系的ConfigAttributes 集合保存在ConfigAttributeDefinition中。這個(gè)類(lèi)只是一個(gè)簡(jiǎn)單的ConfigAttributes存儲(chǔ)器,并不做其它特別的處理。
當(dāng)安全性攔截器收到一個(gè)請(qǐng)求時(shí),它需要判斷使用哪些配置屬性。換句話說(shuō),它需要找到用于這種請(qǐng)求的 ConfigAttributeDefinition.這種判別是通過(guò)ObjectDefinitionSource接口來(lái)處理的.這個(gè)接口提供的主要方法是 public ConfigAttributeDefinition getAttributes(Object object),其中的Object 就是要保護(hù)的安全對(duì)象。因?yàn)榘踩珜?duì)象包含了請(qǐng)求的細(xì)節(jié),所以O(shè)bjectDefinitionSource實(shí)現(xiàn)類(lèi)將能夠抽取它所需要的細(xì)節(jié)來(lái)檢查相關(guān)的 ConfigAttributeDefinition.
1.4 Request Contexts
1.4.1以前的方式
Acegi 安全系統(tǒng)在發(fā)布0.9.0以前,使用ContextHolder來(lái)存儲(chǔ)session間的上下文信息(Context).Context的一個(gè)特殊子類(lèi), SecureContext定義了一個(gè)接口來(lái)存儲(chǔ)認(rèn)證對(duì)象(Authentication object).ContextHolder是一個(gè)線程 (ThreadLocal)類(lèi)型的對(duì)象,有關(guān)Acegi安全系統(tǒng)中線程類(lèi)對(duì)象用法的更全面的討論將放在本文的后續(xù)章節(jié)中.為了保持一致,經(jīng)過(guò)與其它 Spring開(kāi)發(fā)者討論,從0.9.0開(kāi)始,我們將移去ContextHolder和SecureContext.有關(guān)詳情請(qǐng)參考http://article.gmane.org/gmane.comp.java.springframework.devel/8290
和JIRA task SEC-77.這段歷史之所以被提及是因?yàn)镃ontextHolder使用了很長(zhǎng)一段時(shí)間,你所遇到某些有關(guān)Acegi安全系統(tǒng)的文檔可能仍舊說(shuō)到了 ContextHolder.通常你可以用SecurityContextHolder替代ContextHolder,用 SecurityContext替代SecureContext,這樣你就能了解這些文檔的主要意思.
1.4.2 SecurityContext
Acegi 安全系統(tǒng)用SecurityContextHolder來(lái)存儲(chǔ)SecurityContext.SecurityContext包含有 Authentication的getter/setter方法.為了獲得當(dāng)前SecurityContext(或者參與者),所有Acegi安全類(lèi)都要查詢(xún)SecurityContextHolder.SecurityContextHolder是一個(gè)線程,這意味著它與當(dāng)前線程的執(zhí)行有關(guān).
1.4.3 Context Storage
Acegi安全系統(tǒng)設(shè)計(jì)的重點(diǎn)就是在兩個(gè)web請(qǐng)求之間存儲(chǔ)SecurityContextHolder(它是SecurityContext的實(shí)現(xiàn)類(lèi))的內(nèi)容.通過(guò)SecurityContextHolder獲取存儲(chǔ)在SecurityContext內(nèi)Authentication,這樣成功認(rèn)證的參與者就能在隨后的請(qǐng)求中鑒別到.HttpSessionContextIntegrationFilter一直存在到自動(dòng)拷貝一個(gè)定義好 HttpSession屬性的內(nèi)容到SecurityContextHolder中,然后,在每一個(gè)請(qǐng)求的結(jié)尾,再將 SecurityContextHolder的內(nèi)容拷貝回為下一個(gè)請(qǐng)求準(zhǔn)備的HttpSession中.
將HttpSessionContextIntegrationFilter放在其它任何 Acegi安全過(guò)濾器之前(沒(méi)有這樣做是終端用戶(hù)很容易犯的一個(gè)共同的錯(cuò)誤)是必須的.當(dāng)覺(jué)得合適時(shí),Acegi安全過(guò)濾器期望能夠修改 SecurityContextHolder的內(nèi)容,并且如果有必要其它的類(lèi)(也就是 HttpSessionContextIntegrationFilter)將在請(qǐng)求之間存儲(chǔ)這些信息.這就是為什么 HttpSessionContextIntegrationFilter必須是第一個(gè)被用的過(guò)濾器.
通過(guò)設(shè)置HttpSessionContextIntegrationFilter bean中的context屬性,你能定義一個(gè)特定的SecurityContext實(shí)現(xiàn)類(lèi)用到你的應(yīng)用中.
1.4.4 本地化
從1.0.0 開(kāi)始,Acegi安全系統(tǒng)支持異常消息的本地化,這是終端用戶(hù)很希望看到的特性.例如認(rèn)證失敗和存取被否定(授權(quán)失敗)異常的本地化.那些面向開(kāi)發(fā)者或系統(tǒng)配置員的異常和日志(包括不正確的屬性、接口約定違背、使用不正確的構(gòu)造函數(shù)、啟動(dòng)時(shí)間確認(rèn)、調(diào)試級(jí)的日志)等沒(méi)有本地化,而是用英語(yǔ)硬編碼在 Acegi安全系統(tǒng)的代碼內(nèi).
在org.acegisecurity包內(nèi)裝載的acegi-security-xx.jar是一個(gè) messages.properties文件。當(dāng)Acegi安全類(lèi)實(shí)現(xiàn)Spring的MessageSourceAware接口,并且希望消息解析器在應(yīng)用上下文啟動(dòng)時(shí)被依賴(lài)注入時(shí),它就會(huì)用到。通常你所需要做的只是在你的應(yīng)用上下文中注冊(cè)一個(gè)bean來(lái)指向這些消息。下面是一個(gè)示例。
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename"><value>org/acegisecurity/messages</value></property>
</bean>
messages.properties 文件的命名符合標(biāo)準(zhǔn)資源約束,用Acegi安全消息支持的默認(rèn)的語(yǔ)言來(lái)表示并在上述的bean定義中注冊(cè)它。在這個(gè)文件內(nèi)沒(méi)有許多的消息關(guān)鍵詞,因此本地化不應(yīng)認(rèn)為是一個(gè)主要的問(wèn)題。如果你打算對(duì)這個(gè)文件進(jìn)行本地化,請(qǐng)考慮登記一個(gè)JIRA任務(wù)與社區(qū)共享你的工作,并為 messages.properties文件的本地化版本附上一個(gè)適當(dāng)?shù)拿帧?/p>
有關(guān)本地化的討論是一個(gè)叫 org.springframework.context.i18n.LocaleContextHolder的Spring 線程,Acegi通過(guò)使用 Locale來(lái)獲得這個(gè)線程,從而對(duì)一個(gè)消息源中的消息實(shí)行本地化。Spring文檔為你提供了有關(guān)使用LocaleContextHoldert和能自動(dòng)設(shè)置它的幫助類(lèi)(如AcceptHeaderLocaleResolver, CookieLocaleResolver, FixedLocaleResolver, SessionLocaleResolver 等)的更詳細(xì)的情況.
1.5 安全性攔截
1.5.1所有的安全對(duì)象
如在系統(tǒng)設(shè)計(jì)部分描述的那樣,每一個(gè)安全對(duì)象都有它自己的安全性攔截器來(lái)負(fù)責(zé)處理每一個(gè)請(qǐng)求
 |
處理過(guò)程包括以下這些操作:
- 存儲(chǔ)每一個(gè)安全請(qǐng)求的配置屬性。
- 從相應(yīng)的ObjectDefinitionSource提取 ConfigAttributeDefinition應(yīng)用到請(qǐng)求中。
- 從SecurityContext中獲取Authentication對(duì)象,這個(gè)對(duì)象保存在SecurityContextHolder中。
- 傳遞Authentication對(duì)象到AuthenticationManager中,通過(guò)響應(yīng)更新SecurityContextHolder。
- 傳遞Authentication 對(duì)象、ConfigAttributeDefinition和安全對(duì)象到AccessDecisionManager中。
- 傳遞Authentication 對(duì)象、ConfigAttributeDefinition和安全對(duì)象到RunAsManager中。
- 如果RunAsManager對(duì)象返回一個(gè)新的Authentication 對(duì)象,就用它更新SecurityContextHolder對(duì)象。
- 繼續(xù)執(zhí)行安全對(duì)象的請(qǐng)求。
- 如果上一步中RunAsManager對(duì)象返回一個(gè)新的Authentication對(duì)象,就用它更新以前從AuthenticationManager中返回的SecurityContextHolder對(duì)象。
- 如果定義了AfterInvocationManager,把安全對(duì)象的執(zhí)行結(jié)果傳遞給它。從而它可以拋出AccessDeniedException異常,接受可能需要的返回對(duì)象。
- 返回從AfterInvocationManager處收到的任何結(jié)果,或如沒(méi)定義AfterInvocationManager,就返回一個(gè)安全對(duì)象執(zhí)行后提供的簡(jiǎn)單結(jié)果。
|
雖然看起來(lái)有點(diǎn)麻煩,但別擔(dān)心;開(kāi)發(fā)者與安全處理的交互只是簡(jiǎn)單地使用幾個(gè)基本的接口(如AccessDecisionManager)實(shí)現(xiàn)來(lái)完成的。下面我們將詳細(xì)的討論它們。
AbstractSecurityInterceptor處理了上述過(guò)程的大部分。如圖一所示,每個(gè)安全對(duì)象都有它自已的安全性攔截器,它們是AbstractSecurityInterceptor的子類(lèi)。這些安全對(duì)象--具體的安全性攔截器都將在下面詳細(xì)討論。
1.5.2 AOP Alliance(MethodInvocation)安全性攔截器
為了對(duì)MethodInvocations對(duì)象提供保護(hù),開(kāi)發(fā)者只需要簡(jiǎn)單地加上恰當(dāng)配置的方法安全性攔截器(MethodSecurityInterceptor)到你的應(yīng)用中,然后將需要查詢(xún)的bean鏈接到攔截器鏈中去。這種鏈接方法由Spring的 ProxyFactoryBean或BeanNameAutoProxyCreator來(lái)完成,就如Spring的許多其它部分的用法(請(qǐng)參考應(yīng)用實(shí)例)一樣,另外,Acegi安全系統(tǒng)也提供一個(gè)MethodDefinitionSourceAdvisor,它將使用Spring的 DefaultAdvisorAutoProxyCreator自動(dòng)鏈接MethodSecurityInterceptor之前的任何bean到這個(gè)安全性攔截器;MethodSecurityInterceptor本身定義如下:
<bean id="bankManagerSecurity"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes"><value>true</value></property>
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="afterInvocationManager"><ref bean="afterInvocationManager"/></property>
<property name="objectDefinitionSource">
<value>
org.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER
org.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER
</value>
</property>
</bean>
如上所示MethodSecurityInterceptor配置了三個(gè)引用:AuthenticationManager, AccessDecisionManager和RunAsManager。下面我們將分節(jié)討論它們。在這個(gè)例子中我們將定義 AfterInvocationManager,雖然它是可選的。MethodSecurityInterceptor也是通過(guò)"配置屬性"來(lái)配置的,這些配置屬性用于不同的方法簽名。有關(guān)配置屬性的全面的論述已在本文的系統(tǒng)設(shè)計(jì)部分提供。
MethodSecurityInterceptor 有三種通過(guò)配置屬性來(lái)配置的方法,第一種是經(jīng)由屬性編輯器和應(yīng)用上下文,如上所示。第二種使用Jakarta Commons Attributes或 Java 5 Annotation在你的源代碼中通過(guò)定義配置屬性來(lái)配置。第三種方法是你自己寫(xiě)一個(gè)ObjectDefinitionSource,雖然這已超出本文檔的范圍。
不管使用哪種方法,ObjectDefinitionSource都會(huì)相應(yīng)地返回一個(gè)ConfigAttributeDefinition對(duì)象,ConfigAttributeDefinition對(duì)象含有一個(gè)單獨(dú)的安全方法的所有配置屬性。
應(yīng)用注意:MethodSecurityInterceptor.setObjectDefinitionSource()方法實(shí)際需要一個(gè) MethodDefinitionSource實(shí)例,這是一個(gè)標(biāo)志性接口,是ObjectDefinitionSource的一個(gè)子類(lèi)。它簡(jiǎn)單地指示 ObjectDefinitionSource認(rèn)到MethodInvocations。為了簡(jiǎn)單起見(jiàn),我們將繼續(xù)把 MethodDefinitionSource當(dāng)作ObjectDefinitionSource,因?yàn)閷?duì)大多數(shù)使用 MethodSecurityInterceptor用戶(hù)來(lái)說(shuō),區(qū)別很小。
如果使用應(yīng)用上下文屬性編輯器方法(如上所示),要使用逗號(hào)來(lái)區(qū)隔不同應(yīng)用于給定方法模式的配置屬性.每一個(gè)配置屬性都將賦值到它們本身的SecurityConfig對(duì)象中去.SecurityConfig對(duì)象已在系統(tǒng)設(shè)計(jì)部分討論.
如果你使用Jakarta Commons Attributes方式,你的bean上下文配置將會(huì)是不同的,如下所示:
<bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/>
<bean id="objectDefinitionSource"
class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">
<property name="attributes"><ref local="attributes"/></property>
</bean>
<bean id="bankManagerSecurity"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes"><value>false</value></property>
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource"><ref bean="objectDefinitionSource"/></property>
</bean>
另外,你的源代碼將包含akarta Commons Attributes的tag標(biāo)記.用它來(lái)指示ConfigAttribute的一個(gè)具體實(shí)現(xiàn).
下面這個(gè)例子使用SecurityConfig實(shí)現(xiàn)來(lái)表示配置屬性,其結(jié)果與上面用屬性編緝器方式的安全配置一樣.
public interface BankManager {
/**
* @@SecurityConfig("ROLE_SUPERVISOR")
* @@SecurityConfig("RUN_AS_SERVER")
*/
public void deleteSomething(int id);
/**
* @@SecurityConfig("ROLE_SUPERVISOR")
* @@SecurityConfig("RUN_AS_SERVER")
*/
public void deleteAnother(int id);
/**
* @@SecurityConfig("ROLE_TELLER")
* @@SecurityConfig("ROLE_SUPERVISOR")
* @@SecurityConfig("BANKSECURITY_CUSTOMER")
* @@SecurityConfig("RUN_AS_SERVER")
*/
public float getBalance(int id);
}
如果你使用Spring Security Java 5 Annotations方式,你的bean上下文配置如下所示:
<bean id="attributes" class="org.acegisecurity.annotation.SecurityAnnotationAttributes"/>
<bean id="objectDefinitionSource"
class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">
<property name="attributes"><ref local="attributes"/></property>
</bean>
<bean id="bankManagerSecurity"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes"><value>false</value></property>
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource"><ref bean="objectDefinitionSource"/></property>
</bean>
另外,你的源代碼將包含Acegi Java 5 Security Annotations,用它來(lái)表示ConfigAttribute,下面的例子使用@Secured注釋來(lái)表示配置屬性.其結(jié)果與上面用屬性編緝器方式的安全配置相同.
import org.acegisecurity.annotation.Secured;
public interface BankManager {
/**
* Delete something
*/
@Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" })
public void deleteSomething(int id);
/**
* Delete another
*/
@Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" })
public void deleteAnother(int id);
/**
* Get balance
*/
@Secured({"ROLE_TELLER","ROLE_SUPERVISOR","BANKSECURITY_CUSTOMER","RUN_AS_SERVER" })
public float getBalance(int id);
}
也許你已經(jīng)注意到在上面MethodSecurityInterceptor的配置例子中的validateConfigAttributes屬性。當(dāng)設(shè)置為true時(shí)(缺省值),在系統(tǒng)啟動(dòng)時(shí)MethodSecurityInterceptor將會(huì)校驗(yàn)設(shè)置的配置屬性是否有效。它檢查每一個(gè)配置屬性能否被 AccessDecisionManager或RunAsManager處理。如果兩者都不能處理給定的配置屬性,將會(huì)被拋出一個(gè)異常。如果你使用 Jakarta Commons Attributes方式進(jìn)行屬性配置,你應(yīng)將validateConfigAttributes屬性設(shè)置為 false。
注意:當(dāng)使用BeanNameAutoProxyCreator來(lái)創(chuàng)建所需的安全代理時(shí),配置中必須把 proxyTargetClass屬性設(shè)為T(mén)rue.否則,通過(guò)MethodSecurityInterceptor.invoke的檢查方法是代理的調(diào)用者,而不是代理的目標(biāo).請(qǐng)注意這里引入了CGLIB的需求.下面請(qǐng)看BeanNameAutoProxyCreator使用的例子.
<bean id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list><value>methodSecurityInterceptor</value></list>
</property>
<property name="beanNames">
<list><value>targetObjectName</value></list>
</property>
<property name="proxyTargetClass" value="true"/>
</bean>
1.5.3 AspectJ(連接點(diǎn))安全性攔截器
AspectJ安全性攔截器與上一節(jié)討論的AOP Alliance安全性攔截器非常相似.在這節(jié)中我們將只討論它們的不同部分.
AspectJ 攔截器命名為AspectJSecurityInterceptor,不象AOP Alliance安全性攔截器通過(guò)代理依靠Spring應(yīng)用上下文來(lái)設(shè)定那樣,AspectJSecurityInterceptor通過(guò)AspectJ 編譯器來(lái)設(shè)定.在同樣的應(yīng)用中使用這兩種攔截器將很常見(jiàn), AspectJSecurityInterceptor用于域?qū)ο髮?shí)例,而AOP Alliance安全性攔截器用于服務(wù)層的安全.
首先考慮AspectJSecurityInterceptor在Spring應(yīng)用上下文中是怎么配置的:
<bean id="bankManagerSecurity"
class="org.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor">
<property name="validateConfigAttributes"><value>true</value></property>
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="afterInvocationManager"><ref bean="afterInvocationManager"/></property>
<property name="objectDefinitionSource">
<value>
org.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER
org.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER
</value>
</property>
</bean>
如你所見(jiàn),除了類(lèi)名,AspectJSecurityInterceptor與AOP Alliance安全性攔截器完全相同.的確,兩個(gè)攔截器共享同樣的 objectDefinitionSource,因?yàn)閛bjectDefinitionSource是通過(guò) java.lang.reflect.Methods起作用,而不是AOP特定庫(kù)是的類(lèi)來(lái)起作用的.
當(dāng)然,在做存取決定時(shí),你可以通過(guò)相關(guān)的AOP特定庫(kù)調(diào)用來(lái)存取(例如MethodInvocation 或 JoinPoint),也可以考慮通過(guò)一系列附加的證書(shū)來(lái)實(shí)施存取控制.
接下來(lái),需要定義一個(gè)AspectJ的aspect,例如:
package org.acegisecurity.samples.aspectj;
import org.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor;
import org.acegisecurity.intercept.method.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;
public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
private AspectJSecurityInterceptor securityInterceptor;
pointcut domainObjectInstanceExecution(): target(PersistableEntity)
&& execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);
Object around(): domainObjectInstanceExecution() {
if (this.securityInterceptor != null) {
AspectJCallback callback = new AspectJCallback() {
public Object proceedWithObject() {
return proceed();
}
};
return this.securityInterceptor.invoke(thisJoinPoint, callback);
} else {
return proceed();
}
}
public AspectJSecurityInterceptor getSecurityInterceptor() {
return securityInterceptor;
}
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
public void afterPropertiesSet() throws Exception {
if (this.securityInterceptor == null)
throw new IllegalArgumentException("securityInterceptor required");
}
}
在上面的例子中,安全性攔截器應(yīng)用到PersistableEntity的每一個(gè)實(shí)例中,PersistableEntity是一個(gè)抽象類(lèi)沒(méi)有顯示出來(lái)(你可以用任何其它類(lèi)或pointcut類(lèi)來(lái)表示,只要你喜歡).這看起來(lái)有點(diǎn)古怪,proceed()需要調(diào)用AspectJCallback,在 around()函數(shù)體內(nèi)的語(yǔ)句有著特殊的意義.當(dāng)AspectJSecurityInterceptor想要目標(biāo)對(duì)象繼續(xù)執(zhí)行時(shí),它將調(diào)用這個(gè)匿名的 AspectJCallback.
你需要配置Spring來(lái)加載aspect,并把它與AspectJSecurityInterceptor關(guān)聯(lián)起來(lái),完成此任務(wù)的bean聲明如下所示:
<bean id="domainObjectInstanceSecurityAspect"
class="org.acegisecurity.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf">
<property name="securityInterceptor"><ref bean="aspectJSecurityInterceptor"/></property>
</bean>
好了,現(xiàn)在你可以在你應(yīng)用程序的任何地方用你想要適配的任何意思去創(chuàng)建你的beans,它們都會(huì)用到這個(gè)安全性攔截器.
1.5.4 FilterInvocation(過(guò)濾器調(diào)用)安全性攔截器
為了對(duì)FilterInvocation對(duì)象提供保護(hù),開(kāi)發(fā)者需要在web.xml中添加一個(gè)攔截器,來(lái)代理SecurityEnforcementFilter。一個(gè)典型的配置例子如下所示:
<filter>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意,這里這個(gè)filter實(shí)際上是一個(gè)FilterToBeanProxy??從filter到bean的代理。Acegi安全系統(tǒng)中絕大多數(shù)的filter都使用這個(gè)類(lèi)。將會(huì)在filter一節(jié)中講述它的更多內(nèi)容。
在應(yīng)用上下文中,你需要配置三個(gè)bean:
<bean id="securityEnforcementFilter"
class="org.acegisecurity.intercept.web.SecurityEnforcementFilter">
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
<property name="authenticationEntryPoint"><ref bean="authenticationEntryPoint"/></property>
</bean>
<bean id="authenticationEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
<property name="forceHttps"><value>false</value></property>
</bean>
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/secure/super/.*\Z=ROLE_WE_DONT_HAVE
\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
</value>
</property>
</bean>
當(dāng)用戶(hù)請(qǐng)求一個(gè)受保護(hù)的HTTP資源而沒(méi)有通過(guò)認(rèn)證時(shí),authenticationEntryPoint就會(huì)被調(diào)用。這個(gè)類(lèi)負(fù)責(zé)將適當(dāng)?shù)捻憫?yīng)呈現(xiàn)給用戶(hù),因此,開(kāi)始認(rèn)證,Acegi安全系統(tǒng)為認(rèn)證提供了三種具體的認(rèn)證實(shí)現(xiàn): AuthenticationProcessingFilterEntryPoint進(jìn)行表單認(rèn)證; BasicProcessingFilterEntryPoint進(jìn)行HTTP基本認(rèn)證處理; CasProcessingFilterEntryPoint進(jìn)行耶魯集中認(rèn)證服務(wù)(CAS)登錄. AuthenticationProcessingFilterEntryPoint和CasProcessingFilterEntryPoint有與使用HTTPS相關(guān)的可選屬性.如有需要,請(qǐng)參考JavaDoc文檔.
PortMapper提供了有關(guān)哪個(gè)HTTPS端口對(duì)應(yīng)哪個(gè) HTTP端口的信息.AuthenticationProcessingFilterEntryPoint和其它幾個(gè)bean將使用這些信息. PortMapperImpl是其缺省的實(shí)現(xiàn),它將常用的HTTP 80和8080端口映射對(duì)HTTPS相應(yīng)的443和8443端口;如有需要,你可以定制這個(gè)映射.
SecurityEnforcementFilter主要在需要是提供會(huì)話管理支持和認(rèn)證初始化.它把真正的FilterInvocation安全判別到配置到FilterSecurityInterceptor中.
象任何其它安全性攔截器一樣,FilterSecurityInterceptor需要有一份AuthenticationManager, AccessDecisionManager 和 RunAsManager的引用,這些對(duì)象將在下文中分章節(jié)加以討論. FilterSecurityInterceptor也要配置應(yīng)用于不同HTTP URL請(qǐng)求的配置屬性.有關(guān)這些配置屬性的詳細(xì)討論已在本文的系統(tǒng)設(shè)計(jì)章節(jié)是提供.
FilterSecurityInterceptor的配置屬性可以通過(guò)兩種方式來(lái)配置.第一種是使用屬性編輯和應(yīng)用上下文的方式,就如上面顯示的那樣。第二種方式是書(shū)寫(xiě)你的ObjectDefinitionSource,這超過(guò)了本文的范圍。不管使用那種方式, ObjectDefinitionSource負(fù)責(zé)返回一個(gè)ConfigAttributeDefinition對(duì)象,這個(gè)對(duì)象包括了一個(gè)單個(gè) http url請(qǐng)求所關(guān)聯(lián)的所有配置屬性信息。
應(yīng)該注意到的是FilterSecurityInterceptor.setObjectDefinitionSource()實(shí)際盼望得到的是一個(gè)FilterInvocationDefinitionSource實(shí)例。這是一個(gè)繼承了ObjectDefinitionSource的標(biāo)記性接口。它簡(jiǎn)單的表示ObjectDefinitionSource理解FilterInvocations。簡(jiǎn)潔的講,我們只需要繼續(xù)使用 FilterInvocationDefinitionSource,就像使用ObjectDefinitionSource一樣,因?yàn)樗麄冎g的區(qū)別就 FilterSecurityInterceptor的角度來(lái)看是非常小的。
如果使用應(yīng)用上下文屬性編輯的方式(如上所示),應(yīng)用到不同的http rul上的不同配置屬性需要被逗號(hào)分割開(kāi)。每一個(gè)配置屬性都會(huì)分配到它自己的SecurityConfig對(duì)象中。 SecurityConfig對(duì)象在系統(tǒng)設(shè)計(jì)部分已經(jīng)被討論過(guò)。屬性編緝器創(chuàng)建的ObjectDefinitionSource以及 FilterInvocationDefinitionSource要與依賴(lài)于FilterInvocations的配置屬性相匹配. FilterInvocations用于驗(yàn)證所請(qǐng)求的URL表達(dá)是否正確.
在使用配置屬性對(duì)請(qǐng)求的進(jìn)行匹配和處理時(shí),有兩種標(biāo)準(zhǔn)表達(dá)式語(yǔ)法可以使用。缺省的是使用正則表達(dá)式規(guī)則處理所有的表達(dá)式,另外,顯示的 PATTERN_TYPE_APACHE_ANT標(biāo)明將導(dǎo)致所有的表達(dá)式都使用 Apache Ant paths方式進(jìn)行處理。同一定義內(nèi)同時(shí)使用兩種方式是不允許的。例如上面配置可以被Apache Ant paths 方式書(shū)寫(xiě)成以下形式:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
<property name="runAsManager"><ref bean="runAsManager"/></property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/secure/super/**=ROLE_WE_DONT_HAVE
/secure/**=ROLE_SUPERVISOR,ROLE_TELLER
</value>
</property>
</bean>
不管使用哪種表達(dá)式語(yǔ)法,表達(dá)式都總是按照它們定義的順序進(jìn)行處理。所以越詳細(xì)的定義越要放在定義的前面,這十分重要。上面的例子中對(duì)這一點(diǎn)體現(xiàn)的很清楚。更具體的/secure/super/模式出現(xiàn)在沒(méi)那么具體的/secure之前,如果反過(guò)來(lái),將總是與/secure模式匹配,而 /secure/supper將永遠(yuǎn)不會(huì)生效.
特殊的關(guān)鍵字CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON將會(huì)導(dǎo)致 FilterInvocationDefinitionSource在于表達(dá)式進(jìn)行匹配之前會(huì)自動(dòng)轉(zhuǎn)換請(qǐng)求url為小寫(xiě)。缺省情況下請(qǐng)求url是不會(huì)被進(jìn)行轉(zhuǎn)換的,通常情況下建議使用CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON并且書(shū)寫(xiě)表達(dá)式時(shí)都使用小寫(xiě)。
就像其他的安全攔截器一樣,validateConfigAttributes屬性 是可以進(jìn)行設(shè)置的。當(dāng)設(shè)置為true時(shí)(缺省值),在系統(tǒng)啟動(dòng)時(shí) MethodSecurityInterceptor將會(huì)校驗(yàn)設(shè)置的配置屬性是否有效。它檢查每一個(gè)配置屬性能否被 AccessDecisionManager或RunAsManager處理。如果兩者都不能處理給定的配置屬性,將會(huì)被拋出一個(gè)異常.
1.6認(rèn)證
1.6.1 認(rèn)證請(qǐng)求
認(rèn)證需要用一種客戶(hù)端代碼的方式來(lái)把安全性鑒別信息送到Acegi安全系統(tǒng)中.這就是Authentication接口扮演的角色。 Authentication接口包括三個(gè)重要的對(duì)象:principal(調(diào)用者的身份),credentials(證明調(diào)用者身份的證據(jù),例如密碼),授權(quán)給principal的權(quán)限(列表)。principal 和它的credentials有客戶(hù)端代碼提供,授權(quán)列表由認(rèn)證管理器 (AuthenticationManager)提供。
500)this.width=500" align=absMiddle border=0>
如圖3所示,Acegi安全性系統(tǒng)包括幾個(gè)具體的認(rèn)證(Authentication)實(shí)現(xiàn)類(lèi):
UsernamePasswordAuthenticationToken |
允許使用用戶(hù)名(username)和密碼(password)來(lái)表示調(diào)用者(principal)和他的證書(shū)(credentials)。它們也是由 HTTP Session Authentication負(fù)責(zé)創(chuàng)建的。 |
TestingAuthenticationToken |
主要用作單元測(cè)試,它會(huì)為相應(yīng)的AuthenticationProvider自動(dòng)考慮一個(gè)認(rèn)證對(duì)象。 |
RunAsUserToken |
被作為默認(rèn)的run-as認(rèn)證置換實(shí)現(xiàn)。這個(gè)將在Run-As Authentication Replacement那節(jié)中進(jìn)一步討論。 |
CasAuthenticationToken |
用于描述一次成功的耶魯集中認(rèn)證服務(wù) (CAS)。這將在CAS章節(jié)中進(jìn)一步討論。 |
PrincipalAcegiUserToken 和JettyAcegiUserToken |
實(shí)現(xiàn)了AuthByAdapter (Authentication的子類(lèi))并且它們是在認(rèn)證過(guò)程已經(jīng)被 Acegi系統(tǒng)的容器適配器完成后被使用的。這部分也將在容器適配器章節(jié)中作進(jìn)一步討論。 |
對(duì)于主體(principal)的授權(quán)工作是通過(guò)GrantedAuthority進(jìn)行的。GrantedAuthority接口將在下面關(guān)于授權(quán)的章節(jié)中被討論。
1.6.2 認(rèn)證管理器
在安全攔截器一節(jié)中我們提到AbstractSecurityInterceptor從SecurityContextHolder中的 SecurityContext里面提取Authentication對(duì)象。緊接著就將它傳遞給AuthenticationManager。 AuthenticationManager接口十分的簡(jiǎn)單:
public Authentication authenticate(Authentication authentication) throws AuthenticationException;
AuthenticationManager 的實(shí)現(xiàn)在認(rèn)證失敗的時(shí)候會(huì)拋出一個(gè)異常(AuthenticationException),或者成功時(shí)返回一個(gè)組裝成功的Authentication 對(duì)象。特別要注意,返回的Authentication對(duì)象應(yīng)該包括一組GrantedAuthority對(duì)象。安全攔截器將返回的、組裝后的 Authentication對(duì)象放回到SecurityContextHolder中的SecurityContext里,用以替換原來(lái)的那個(gè) Authentication對(duì)象。
AuthenticationException有幾個(gè)子類(lèi),其中最重要的是 BadCredentialsException (主體(principal)或者(證據(jù))credentials錯(cuò)誤), DisabledException和LockedException.后面兩個(gè)異常表示主體已經(jīng)發(fā)現(xiàn)(principal)但是證據(jù)沒(méi)有通過(guò)檢驗(yàn)還有認(rèn)證被否決、拒絕。AuthenticationServiceException也會(huì)被使用,它表明認(rèn)證系統(tǒng)不能處理請(qǐng)求(例如數(shù)據(jù)庫(kù)不可用引起)。 AuthenticationException 還有CredentialsExpiredException和 AccoungtExpiredException兩個(gè)子類(lèi),雖然它們不是很常用。
1.6.3 基于供方的認(rèn)證
盡管最基本的Authentication和AuthenticationManager接口使用戶(hù)可以開(kāi)發(fā)出自己的認(rèn)證系統(tǒng),但是用戶(hù)完全可以考慮使用 Acegi安全系統(tǒng)提供的基于供方的認(rèn)證包。其關(guān)鍵類(lèi)??ProviderManager通過(guò)一組認(rèn)證提供者 (AuthenticationProviders)的形式在bean定義中被配置:
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="someOtherAuthenticationProvider"/>
</list>
</property>
</bean
ProviderManager 將調(diào)用一系列已注冊(cè)的AuthenticationProvider實(shí)現(xiàn),直到找到一個(gè)表明它能夠認(rèn)證給定的Authentication類(lèi)為止。當(dāng)找到第一個(gè)符合條件的AuthenticationProvider時(shí),它就把它傳遞給認(rèn)證請(qǐng)求。AuthenticationProvider要么拋出一個(gè) AuthenticationException異常,要么返回一個(gè)組裝成功的Authentication對(duì)象.
ProviderManager 還有幾個(gè)其它重要的函數(shù),它集成了并發(fā)會(huì)話處理支持,它也能將任何由AuthenticationProvider拋出的異常轉(zhuǎn)化和發(fā)布一個(gè)恰當(dāng)?shù)氖录_@些事件可在org.acegisecurity.event.authentication包中找到。高級(jí)用戶(hù)能夠配置 ProviderManager.exceptionMappings屬性從而將不同的異常映射為不同的事件(通常不需要這么做,缺省的事件傳播是恰當(dāng)?shù)??特別是當(dāng)你沒(méi)在ApplicationContext中配置ApplicationListener時(shí),事件就會(huì)簡(jiǎn)單地忽略掉)
Acegi安全系統(tǒng)所提供的幾個(gè)AuthenticationProvider實(shí)現(xiàn)類(lèi):
TestingAuthenticationProvider 能夠?qū)estingAuthenticationToken進(jìn)行驗(yàn)證。這種認(rèn)證的限制是它不管TestingAuthenticationToken中包含什么,它都認(rèn)為是有效的。這就使得它在進(jìn)行單元測(cè)試時(shí)變得非常有用,因?yàn)槟憧梢詣?chuàng)建一個(gè)Authentication對(duì)象---它正好對(duì)你需要進(jìn)行測(cè)試的方法進(jìn)行了授權(quán)。這樣你就可以不需要在產(chǎn)品系統(tǒng)中注冊(cè)AuthenticationProvider而能完成需要的測(cè)試環(huán)境搭建了。
DaoAuthenticationProvider |
可以對(duì)通過(guò)對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)的存取,驗(yàn)證UsernamePasswordAuthenticationToken。這個(gè)方面將在下面的章節(jié)進(jìn)行討論,因?yàn)樗沁M(jìn)行驗(yàn)證處理的主要工作方式。 |
RunAsImplAuthenticationProvider |
用以驗(yàn)證RunAsUserToken。這個(gè)將在下面Run-As Authentication Replacement章節(jié)進(jìn)行討論。如果你不使用 run-as處理機(jī)制,你不需要注冊(cè)此AuthenticationProvider。 |
AuthByAdapterProvider |
能夠驗(yàn)證任何的AuthByAdapter (a subclass of Authentication used with container adapters)。這部分內(nèi)容將在下面的章節(jié)進(jìn)行討論。如果你不使用容器適配器,你不需要注冊(cè)此AuthenticationProvider。 |
CasAuthenticationProvider |
用來(lái)驗(yàn)證CAS,將在下面章節(jié)進(jìn)行討論。 |
JaasAuthenticationProvider |
將會(huì)將認(rèn)證請(qǐng)求指派到JAAS登錄模塊,這將在下文進(jìn)行討論。 |
1.6.4 并發(fā)會(huì)話支持
Acegi安全系統(tǒng)具有阻止同一主體并發(fā)訪問(wèn)同一web應(yīng)用資源的能力。例如:你可以阻止用戶(hù)"batman"同一時(shí)間內(nèi)兩次進(jìn)行系統(tǒng)登錄。
為了使用并發(fā)會(huì)話支持,你需要在web.xml是加入下列語(yǔ)句:
<listener>
<listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
</listener>
另外,你需要將org.acegisecurity.concurrent.ConcurrentSessionFilter加入到你的 FilterChainProxy中。并發(fā)會(huì)話過(guò)濾器需要配置唯一一個(gè)sessionRegistry的屬性,它通常指向 SessionRegistryImpl實(shí)例。
每次HttpSession開(kāi)始或終止時(shí),web.xml中的 HttpSessionEventPublish都會(huì)產(chǎn)生一個(gè)ApplicationEvent事件并通知Spring的 ApplicationContext.這非常關(guān)鍵,因?yàn)樗试SSessionRegistryImpl注意到會(huì)話會(huì)在何時(shí)結(jié)束。
你也需要建立ConcurrentSessionControllerImpl,并在ProviderManager中引用它。
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
</property>
<property name="sessionController"><ref bean="concurrentSessionController"/></property>
</bean>
<bean id="concurrentSessionController" class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl">
<property name="maximumSessions"><value>1</value></property>
<property name="sessionRegistry"><ref local="sessionRegistry"/></property>
</bean>
<bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl"/>
1.6.5 數(shù)據(jù)存取對(duì)象認(rèn)證提供者
Acegi 安全系統(tǒng)包含一個(gè)產(chǎn)品級(jí)的認(rèn)證提供者(AuthenticationProvider)實(shí)現(xiàn)??DaoAuthenticationProvider,這個(gè)認(rèn)證提供者通過(guò)獲得在創(chuàng)建bean時(shí)配置在數(shù)據(jù)存取對(duì)象中的認(rèn)證詳細(xì)信息,從而能夠認(rèn)證 UsernamePasswordAuthenticationToken
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService"><ref bean="inMemoryDaoImpl"/></property>
<property name="saltSource"><ref bean="saltSource"/></property>
<property name="passwordEncoder"><ref bean="passwordEncoder"/></property>
</bean>
其中PasswordEncoder和SaltSource是可選的,PasswordEncoder負(fù)責(zé)對(duì)認(rèn)證庫(kù)中的密碼進(jìn)行加解密.而 SaltSource則是在產(chǎn)生密碼時(shí)給它加點(diǎn)"鹽",以增強(qiáng)密碼在認(rèn)證庫(kù)中的安全性.Acegi安全系統(tǒng)提供的PasswordEncoder實(shí)現(xiàn)中包括MD5、SHA和明文編碼。Acegi安全系統(tǒng)也提供兩個(gè)SaltSource的實(shí)現(xiàn):SystemWideSaltSource,它用同樣的"鹽"對(duì)系統(tǒng)中所有的密碼進(jìn)行編碼,而ReflectionSaltSource只對(duì)從UserDetails對(duì)象返回的獲得這種"鹽"的指定屬性進(jìn)行檢查。請(qǐng)參考JavaDoc以獲取對(duì)這些可選特性的更詳細(xì)信息。
除上屬性外,DaoAuthenticationProvider也支持對(duì) UserDetails對(duì)象的緩存。UserCache接口使DaoAuthenticationProvider能把UserDetails對(duì)象放進(jìn)緩存并可在接下來(lái)對(duì)相同用戶(hù)進(jìn)行認(rèn)證時(shí)在緩存中找回它。在缺省時(shí)DaoAuthenticationProvider使用NullUserCache,表示沒(méi)有使用Cache.Acegi也提供了一個(gè)可用的緩存實(shí)現(xiàn)??EhCacheBasedUserCache,其配置如下:
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService"><ref bean="userDetailsService"/></property>
<property name="userCache"><ref bean="userCache"/></property>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:/ehcache-failsafe.xml</value>
</property>
</bean>
<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>userCache</value>
</property>
</bean>
<bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache"><ref local="userCacheBackend"/></property>
</bean>
所有Acegi安全系統(tǒng)的EH-CACHE實(shí)現(xiàn)中(包括EhCacheBasedUserCache),都需要一個(gè)EH-CACHE cache對(duì)象,這個(gè) cache對(duì)象可在你喜歡的任何地方獲得,雖然我們推薦你使用Spring的工廠類(lèi)如上配置中所示。如使用Spring的工廠類(lèi),請(qǐng)參考Spring文檔以獲取如何優(yōu)化的更詳細(xì)的細(xì)節(jié),如緩存存儲(chǔ)本地化、內(nèi)存使用、退出策略、超時(shí)設(shè)定等。
一個(gè)能為DaoAuthenticationProvider提供存取認(rèn)證庫(kù)的的類(lèi),它必須要實(shí)現(xiàn)UserDetailsService接口:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException;
UserDetails 是一個(gè)接口,它能提供一系列g(shù)et函數(shù)以獲得認(rèn)證時(shí)需要的基本認(rèn)證信息如用戶(hù)名、密碼、所獲得的授權(quán)、是否禁用等;一個(gè)具體的實(shí)現(xiàn)就是User類(lèi). Acegi用戶(hù)需要決定什么時(shí)候?qū)懰麄兊腢serDetailsService接口以及返回的UserDetails是什么類(lèi)型。在大多數(shù)情況下將使用 User及其子類(lèi),雖然在特定的環(huán)境中(如對(duì)象關(guān)系映射器)也許需要用戶(hù)寫(xiě)他們自己的UserDetails實(shí)現(xiàn)。UserDetails也經(jīng)常用于存儲(chǔ)一些與調(diào)用者相關(guān)的附加信息(如電話號(hào)碼、email地址等)因此它們經(jīng)常在web視圖中使用。
如果UserDetailsService實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,用戶(hù)應(yīng)當(dāng)很容易從他們選擇的持久化策略中找回認(rèn)證信息。
DaoAuthenticationProvider設(shè)計(jì)沒(méi)有考慮支持帳戶(hù)鎖定的功能,因?yàn)槟菍⒃黾覷serDetailsService接口的復(fù)雜性。例如需要增加不成功認(rèn)證的計(jì)數(shù),這些功能可能通過(guò)補(bǔ)充應(yīng)用程序的公布特性而很容易提供,這些特性將在下面討論。
DaoAuthenticationProvider 返回一個(gè)Authentication對(duì)象,它含有principal的屬性集。principal要么是一個(gè)字串(是必需的用戶(hù)名),要么是一個(gè) UserDetails對(duì)象(從UserDetailsService中查找),缺省情況下返回UserDetails。因?yàn)檫@將使應(yīng)用能加上額外的屬性以便在應(yīng)用中使用,如用戶(hù)的全名,Email地址等。如使用容器適配器,或在應(yīng)用中使用Strings(在Acegi 0.6之前的版本就是這樣)你就應(yīng)在你的應(yīng)用上下文中把 DaoAuthenticationProvider.forcePrincipalAsString屬性設(shè)為true。
1.6.6 In-Memory 認(rèn)證
雖然使用DaoAuthenticationProvider,從持久化引擎中獲取信息來(lái)創(chuàng)建定制的UserDetailsService實(shí)現(xiàn)很容易,但許多應(yīng)用不需要這么復(fù)雜,一種替代方式是使用InMemoryDaoImpl在應(yīng)用上下文中配置認(rèn)證庫(kù):
<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
dianne=emu,ROLE_TELLER
scott=wombat,ROLE_TELLER
peter=opal,disabled,ROLE_TELLER
</value>
</property>
</bean>
1.6.7 JDBC認(rèn)證
1.6.8 JAAS認(rèn)證
1.6.9 Siteminder認(rèn)證
1.6.10 使用認(rèn)證的幾點(diǎn)建議
---------------------------------------------------------------------------------------------------------------------------------
說(shuō)人之短,乃護(hù)己之短。夸己之長(zhǎng),乃忌人之長(zhǎng)。皆由存心不厚,識(shí)量太狹耳。能去此弊,可以進(jìn)德,可以遠(yuǎn)怨。
http://m.tkk7.com/szhswl
------------------------------------------------------------------------------------------------------ ----------------- ---------
posted on 2007-12-21 09:20
宋針還 閱讀(474)
評(píng)論(0) 編輯 收藏 所屬分類(lèi):
ACEGI