張昊

          J-Hi(http://www.j-hi.net)

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            45 Posts :: 1 Stories :: 110 Comments :: 0 Trackbacks

          首先描述最簡(jiǎn)單的身份驗(yàn)證后臺(tái)的處理過(guò)程

          1、 用戶輸入需要確認(rèn)自己身份的信息,如賬號(hào)與密碼或許需要其它信息

          2、 也許你希望提供多種有方式的驗(yàn)證形式,由系統(tǒng)選擇最合適的那一種,如從數(shù)據(jù)庫(kù)(DAO)或是通過(guò)身份驗(yàn)證中心的服務(wù)(ACS)統(tǒng)一管理等

          3、 根據(jù)賬號(hào)找到指定源中的用戶、密碼及所擁有的權(quán)限(如果沒(méi)找到就拋出異常)等用戶的身份信息,驗(yàn)證用戶輸入的信息與源中返回的信息是否一致(如密碼)

          4、 為了不至于每次都重復(fù)上面的過(guò)程,還要將驗(yàn)證后的信息緩存起來(lái),如放到session

          acegi結(jié)合分析

          1、 用戶提交后為了能進(jìn)行上述的處理,acegi不是通過(guò)請(qǐng)求-影響模式,而是能過(guò)ServletFilter實(shí)現(xiàn)的。可以理解成這個(gè)身份驗(yàn)證的主入口,不同的處理機(jī)制采用不同的Filter,類名的規(guī)則為*ProcessingFilter,這些類全部都繼承至javax.servlet.Filter接口。下面是幾個(gè)常用的過(guò)濾器

          模塊

          功能

          BasicProcessingFilter

          按照RFC1945處理基本的身份驗(yàn)證請(qǐng)求

          CasProcessingFilter

          處理耶魯大學(xué)的中心身份驗(yàn)證服務(wù)器(CAS)許可

          AnonymousProcessingFilter

          對(duì)匿名身份的處理

          AuthenticationProcessingFilter

          處理類似于Servlet規(guī)范的j_security_checkHttp form post

          Spring的配置過(guò)程中有些過(guò)濾器除了自身的信息外,還有可能需要切入點(diǎn),以便切入到其它模塊中的配置信息。如AuthenticationProcessingFilter驗(yàn)證不通過(guò)的處理頁(yè)面或要采用的協(xié)議、CasProcessingFilter需要提交到CAS上的其他信息。它們的統(tǒng)一接口類為AuthenticationEntryPoint在此要注意的是,切入點(diǎn)與過(guò)濾器之間有著密切的聯(lián)系與對(duì)應(yīng)關(guān)系的。

          2、 acegi為了實(shí)現(xiàn)多種方式的驗(yàn)證形式提供了AuthenticationManager接口,目的是對(duì)多種驗(yàn)證形式進(jìn)行管理,并通過(guò)指定的驗(yàn)證形式返回用戶的身份。所以上述的過(guò)濾器均會(huì)調(diào)用該接口的authenticate(Authentication authentication)方法以返回當(dāng)前用戶的身份信息。該接口主要的實(shí)現(xiàn)類是ProviderManager

          AuthenticationProvider(身份驗(yàn)證供應(yīng)器)接口有很多實(shí)現(xiàn)類,每個(gè)都致力于處理一個(gè)特定身份驗(yàn)證的具體實(shí)現(xiàn),AuthenticationManager負(fù)責(zé)輪詢AuthenticationProvider列表,并使用第一個(gè)能夠處理給定的Authentication請(qǐng)求對(duì)象的AuthenticationProvider。身份驗(yàn)證供應(yīng)器也就是上面說(shuō)的驗(yàn)證形式。最常用的是DaoAuthenticationProvider,實(shí)現(xiàn)從數(shù)據(jù)庫(kù)中獲取用戶的身份信息

          3、 acegi中用戶的身份信息存放到以Authentication接口的實(shí)現(xiàn)類的實(shí)例中。在介紹該接口之前先要了解一些關(guān)鍵術(shù)語(yǔ)

          1) principal(主體)指可以執(zhí)行操作的用戶、服務(wù)或代理(agent),即身份驗(yàn)證的發(fā)啟者

          2) credential(憑證)指主體提供密碼之類用于身份驗(yàn)證(authentication)的信息戳

          3) authentication(身份驗(yàn)證)確定調(diào)用者身份的過(guò)程

          4) authorization(授權(quán))指決定哪個(gè)主體準(zhǔn)許執(zhí)行給定操作的過(guò)程

          Authentication接口包含主體標(biāo)示、主體的憑證以及主體所擁有的一組權(quán)限。

          Authentication接口繼承java.security.Principal接口,因此Authentication天生就要滿足主體的類型。該接口實(shí)際上是上述幾個(gè)關(guān)鍵詞對(duì)象化的一個(gè)容器,它存儲(chǔ)了身份驗(yàn)證的全部信息。由此會(huì)發(fā)現(xiàn)它與AuthenticationProvider(身份驗(yàn)證供應(yīng)器)有著天然的聯(lián)系,如對(duì)于數(shù)據(jù)庫(kù)的供應(yīng)器它只要記錄用戶的帳號(hào)和密碼,但對(duì)于CAS它還可能還要記錄服務(wù)器響應(yīng)的列表,或是與認(rèn)證相關(guān)的其它信息。因此該接口對(duì)應(yīng)身份驗(yàn)證供應(yīng)器有多個(gè)實(shí)現(xiàn)類。正因?yàn)橛诖耍械膶?shí)現(xiàn)類對(duì)于開(kāi)發(fā)者來(lái)說(shuō)幾乎是隱藏在,可以理解成工具或是服務(wù)類

          下面對(duì)Authentication接口中的方法做詳細(xì)擴(kuò)展說(shuō)明

          1) getPrincipal(獲得主體)getCredential(獲得憑證),它們的設(shè)置過(guò)程:無(wú)論何種驗(yàn)證形式用戶都是通過(guò)用戶輸入帳號(hào)和密碼開(kāi)始的,過(guò)濾器會(huì)攔截用戶的請(qǐng)求,由驗(yàn)證管理器(AuthenticationManager)找到合適的身份驗(yàn)證供應(yīng)器(AuthenticationProvider)提供身份驗(yàn)證。在AuthenticationManager接口只有一個(gè)方法authenticate(…),而該方法的參數(shù)與返回類型均是Authentication。所以大多數(shù)過(guò)濾器在請(qǐng)求身份驗(yàn)證前總是創(chuàng)建一個(gè)簡(jiǎn)單的UsernamePasswordAuthenticationToken(該類是Authentication接口的子類)對(duì)象,將其作為AuthenticationManager.authenticate(…)方法中的參數(shù),再由AuthenticationProvider依據(jù)自身的特點(diǎn)創(chuàng)建相應(yīng)的Authentication接口子類的實(shí)例,因此身份驗(yàn)證的具體信息也是在這最后一步完成的,即是在AuthenticationProvider中被創(chuàng)建與賦值的。其中就包括主體與憑證。如對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō)也是賬號(hào)與密碼。

          2) getAuthorities()得到當(dāng)前用戶所擁有的全部權(quán)限。Acegi提供一個(gè)GrantedAuthority接口,用于對(duì)應(yīng)應(yīng)用系統(tǒng)中有真正意義的權(quán)限實(shí)現(xiàn)。常用的是GrantedAuthorityImpl,它存儲(chǔ)主體的已獲授權(quán)的String表示,即權(quán)限也就是字符串的表示。而GrantedAuthority僅對(duì)字符做一層簡(jiǎn)單的封裝。

          3) getDetails()獲取詳細(xì)資料,目的是得到當(dāng)前用戶的詳細(xì)信息,如在應(yīng)用系統(tǒng)可以是用戶的一個(gè)Bean(所在的部門,真實(shí)姓名…)。為此Acegi提供了一個(gè)UserDetails接口.

          l         isAccountNonExpired         帳號(hào)是否未過(guò)期

          l         isAccountNonLocked           帳號(hào)是否未鎖定

          l         isCredentialsNonExpired密碼是否未過(guò)期

          l         isEnabled                             是否可用(激活)

          我們會(huì)發(fā)現(xiàn)對(duì)于一個(gè)應(yīng)用系統(tǒng)來(lái)說(shuō),用戶的詳細(xì)信息中除與身份驗(yàn)證相關(guān)的信息外還要很多其它的信息,如用戶所在的部門、電話。為此acegi提供了另一個(gè)接口AuthenticationDetailsSource,它僅提供一個(gè)方法buildDetails(HttpServletRequest request)用以創(chuàng)建客戶化的用戶信息。在過(guò)濾器中在執(zhí)行身份驗(yàn)證之前調(diào)用該方法。

          4) isAuthenticated(),是否通過(guò)了身份驗(yàn)證

          當(dāng)通過(guò)主體(帳號(hào))從源(DAOCASJAAS…)中返回的信息中創(chuàng)建出Authentication實(shí)例后,還要校驗(yàn)用戶的憑證(密碼)的一致性。對(duì)于提供了以PasswordEncoder接口為代表的一組密碼編碼器的實(shí)現(xiàn)

          4、 在我們討論對(duì)于身份驗(yàn)證(Authentication)對(duì)象的保存之前,先來(lái)說(shuō)明一下對(duì)它的訪問(wèn)。為了確保任何對(duì)身份驗(yàn)證感興趣的類型都可以訪問(wèn)它,acegi通過(guò)SecurityContextHolder(該類中提供的所有成員方法均是靜態(tài)的)用來(lái)保持SecurityContextThreadLocal對(duì)象。

          通過(guò)SecurityContext類圖可以看出在它很簡(jiǎn)單只是對(duì)Authentication對(duì)象的一個(gè)引用,之所以又做了這樣一層包裝的目的是為了客戶可以做任意的擴(kuò)展。由于SecurityContextHolder引用的是線程ThreadLocal集合,而對(duì)于B/S系統(tǒng)來(lái)說(shuō),一個(gè)線程的生命期還是太短了,無(wú)法保證SecurityContext對(duì)象的信息在一個(gè)線程執(zhí)行后不會(huì)被JVM垃圾回收。一般來(lái)說(shuō)我們會(huì)將SecurityContext放到session中,但這樣又無(wú)法保證在邏輯層中訪問(wèn)到session。對(duì)此acegi的解決方案是通過(guò)javax.servlet.Filter使不同與的訪問(wèn)范圍與SecurityContextHolder做整合(session為例,每次請(qǐng)求過(guò)濾器都會(huì)從session中取得SecurityContext然后再將其賦到SecurityContextHolder)Acegi提供了多種整合過(guò)濾器,命名規(guī)則為*IntegrationFilter

          模塊

          功能

          HttpSessionContextIntegrationFilter

          請(qǐng)求之間使用HttpSession存儲(chǔ)SecurityContext

          HttpRequestIntegrationFilter

          HttpServletRequest.getUserPrincipal()獲得身份驗(yàn)證,但在請(qǐng)求結(jié)束時(shí)不能將該身份驗(yàn)證寫回該位置

          JbossIntegrationFilter

          Jbossjava:comp/env/security/subjectJNDI位置獲取身份驗(yàn)證,但在請(qǐng)求結(jié)束時(shí)不能將該身份驗(yàn)證寫回該位置

          接下來(lái)再來(lái)討論對(duì)Authentication的設(shè)置,當(dāng)處理過(guò)濾器調(diào)用AuthenticationManager. Authentication(…)獲得Authentication對(duì)象后,它會(huì)調(diào)用自身的成員方法successfulAuthentication(…)將其封裝為SecurityContext并設(shè)置到SecurityContextHolder中。

          注意successfulAuthentication(…)方法是在抽象類AbstractProcessingFilter中,而有一些處理過(guò)程器并沒(méi)有繼承該類,所以只有AbstractProcessingFilter的子類才會(huì)在當(dāng)前線程中取得SecurityContext對(duì)象。

          再來(lái)描述授權(quán)的后臺(tái)的處理過(guò)程

          1、 我們可能希望在三個(gè)地方做安全驗(yàn)證,即是否可以訪問(wèn)當(dāng)前頁(yè)面,是否可以調(diào)用當(dāng)前方法,是否可以控制當(dāng)前方法參數(shù)的對(duì)象域

          2、 對(duì)于安全驗(yàn)證的判斷可能希望提供不同的表決策略,如一票否決制,還是有一票同意就通過(guò)或是同意大于否決票時(shí)才通過(guò)。

          3、 總之,所有需要的處理無(wú)非是在做這樣的工作。當(dāng)前用戶所擁有的權(quán)限中,否則有(或匹配)與當(dāng)前被調(diào)用者(頁(yè)面、方法、域)所指定的權(quán)限

          Acegi對(duì)于授權(quán)的技術(shù)實(shí)現(xiàn)

          從技術(shù)上話,授權(quán)完全與日志一樣是一個(gè)橫切關(guān)注點(diǎn),與具體業(yè)務(wù)沒(méi)有任何關(guān)系。而Acegi只是借助SpringAOPjavax.servlet.Filter,對(duì)這授權(quán)方面的攔截。對(duì)于攔截器與其持有的對(duì)象Acegi提供如下實(shí)現(xiàn):

          攔截器

          描述

          持有的對(duì)象

          描述

          AbstractSecurityInterceptor

          是所有攔截器的抽象父類,實(shí)際授權(quán)的全部驗(yàn)證過(guò)程均在該類beforeInvocation()afterInvocation()兩個(gè)方法中完成,子類只是區(qū)別不同類型的安全對(duì)象。

          FilterSecurityInterceptor

          是一個(gè)web的過(guò)濾器,用于對(duì)頁(yè)面(URL)的授權(quán)驗(yàn)證

          FilterInvocation

          僅是對(duì)簡(jiǎn)單crequestresponseFilterChain包裝的實(shí)現(xiàn)

          MethodSecurityInterceptor

          用于驗(yàn)證Spring容器中bean的方法,并且需要使用Spring中的代理

          MethodInvocation

          AOP Alliance提供的被代理對(duì)象接口,運(yùn)行時(shí)的實(shí)際對(duì)象類型是由Spring提供的ReflectiveMethodInvocationCglibMethodInvocation

          AspectJSecurityInterceptor

          該類是在指定切入點(diǎn)調(diào)用AspectJ

          JoinPoint AspectJCallback

          執(zhí)行AspectJ的回調(diào)






          AbstractSecurityInterceptor的執(zhí)行過(guò)程:

          首先查詢應(yīng)用于該調(diào)用的配置屬性。如果沒(méi)有任何配置屬性,該調(diào)用被認(rèn)為是公有的,并且繼續(xù)進(jìn)行該調(diào)用。如果找到配置屬性,包含在SecurityContextHolder中的Authentication是通過(guò)AuthenticationManager驗(yàn)證的,并且請(qǐng)求AccessDecisionManager來(lái)批準(zhǔn)該請(qǐng)求,如果成功RunAsManager可以替代該Authentication的標(biāo)識(shí),然后繼續(xù)進(jìn)行該調(diào)用。調(diào)用完成時(shí),將通過(guò)更新SecurityContextHolder來(lái)包含實(shí)際的身份驗(yàn)證對(duì)象以清除RunAsManager的替代標(biāo)識(shí)。最后調(diào)用AfterInvocationManager,如果定義了的話。

          RunAsManager:該接口的主要作用成員方法是Authentication buildRunAs(Authentication authentication, Object object, ConfigAttributeDefinition config)目的是當(dāng)執(zhí)行特定操作時(shí)用于選擇性的替換Authentication對(duì)象,該方法是在安全驗(yàn)證之后,安全對(duì)象執(zhí)行之前被調(diào)用。在安全對(duì)象執(zhí)行之后該方法對(duì)Authentication對(duì)象替換后被還原。

          AfterInvocationManager:作用是修改從安全對(duì)象調(diào)用中返回的對(duì)象。這通常被用來(lái)過(guò)濾僅針對(duì)已包含、已驗(yàn)證元素的集合,或者如果信息是受保護(hù)的,轉(zhuǎn)換實(shí)例變量。如果該主體沒(méi)有針對(duì)返回對(duì)象的權(quán)限,它還可以拋出AccessDeniedException。主要的方法是

          Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,        Object returnedObject) throws AccessDeniedException,它需要返回一個(gè)對(duì)象,然后該對(duì)象將變成安全對(duì)象調(diào)用的結(jié)果。輪詢AfterInvocationProvider接口的具體類。

          最后分析權(quán)限表決的原理,還是讓我們虛擬這樣一個(gè)場(chǎng)景:

          1、 要求有兩個(gè)表決器,1)判斷當(dāng)前用戶是否為超級(jí)管理員,2)判斷當(dāng)前用戶是否擁有當(dāng)前所調(diào)用的方法或頁(yè)面的權(quán)限

          2、 如果其中任何一個(gè)表決器投出贊成票,就認(rèn)為通過(guò)。這是一種表決策略

          Acegi結(jié)合分析

          2、表決策略作用是輪詢所有的表決器,根據(jù)當(dāng)前策略結(jié)合表決器的表決結(jié)果,做出最終判斷結(jié)果。Acegi表決策略管理的接口是

          1)      decide(…)根據(jù)當(dāng)前用戶所擁有的權(quán)限信息[authentication],攔截器所持有的對(duì)象[object]以及在配置文件中所持有對(duì)象所必要的權(quán)限s[config],輪詢表決器做出決策。

          2)      supports(ConfigAttribute)判斷所有表決器是否有一個(gè)以上支持這種權(quán)限的表示形式

          3)      supports(Class) 判斷所有表決器是否有一個(gè)以上支持?jǐn)r截器所持有的對(duì)象類型

          后兩個(gè)方法主要在是各種攔截器初始化時(shí)調(diào)用,以但Spring驗(yàn)證bean定義的合法性

          對(duì)于表決策略來(lái)說(shuō)最終可能會(huì)得到三個(gè)結(jié)果中的一個(gè)贊成、否決或是棄權(quán)(結(jié)果是由表決器結(jié)合策略得到的)。對(duì)于棄權(quán)的產(chǎn)生是由于攔截器所持有的對(duì)象在配置中沒(méi)有設(shè)置任何權(quán)限。為此該接口的抽象子類提供allowIfAllAbstainDecisions開(kāi)關(guān),如果所有表決器投出的均為棄權(quán)票,表示沒(méi)有通過(guò)授權(quán)則將該屬性設(shè)為true,默認(rèn)為false.

          Acegi提供了多種表決策略

          注意:一個(gè)表決器只能投一票

          AffirmativeBased       有一個(gè)決策器投出贊成票就通過(guò)

          ConsensusBased          贊成票大于否決票就通過(guò),相同也算是沒(méi)有通過(guò)授權(quán)

          UnanimousBased        一票否決制,也就是只要有一個(gè)表決器投否決票就算不通過(guò)

          因此對(duì)于上面的場(chǎng)景我們應(yīng)該選擇AffirmativeBased表決策略,在決策略下還要有兩個(gè)表決器,下面介紹表決器

          1

          posted on 2011-03-31 19:07 張昊 閱讀(1985) 評(píng)論(1)  編輯  收藏

          Feedback

          # re: 通過(guò)場(chǎng)景分析acegi的設(shè)計(jì)原理 2011-03-31 23:07 popoer
          寫得不錯(cuò)~  回復(fù)  更多評(píng)論
            


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 温州市| 常熟市| 黄浦区| 察雅县| 利川市| 运城市| 洛阳市| 上蔡县| 泸州市| 进贤县| 双牌县| 理塘县| 南溪县| 蓬安县| 沅陵县| 安图县| 锡林浩特市| 崇明县| 科尔| 六枝特区| 洪湖市| 万全县| 云安县| 鸡泽县| 民丰县| 平邑县| 山丹县| 五大连池市| 清苑县| 三明市| 麻栗坡县| 肇东市| 汕头市| 威宁| 香河县| 包头市| 松原市| 阳信县| 诸城市| 青州市| 祁门县|