<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
    對ACEGI中FilterChainProxy進行性能調優

    一般情況下,在ACEGI中隊filterChainProxy如下配置

        <bean id="filterChainProxy"

              class="org.acegisecurity.util.FilterChainProxy">

            <property name="filterInvocationDefinitionSource">

                <value>

                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON

                    PATTERN_TYPE_APACHE_ANT

                    /**=httpSessionContextIntegrationFilter,captchaValidationProcessingFilter,

    authenticationProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,

    logoutFilter,channelProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,

    exceptionTranslationFilter,filterInvocationInterceptor

                </value>                      

            </property>

    </bean>

    當系統中受保護的Resource過多時,會出現這樣的一個問題,頁面加載速度明顯變慢了,特別是在用戶登錄系統后。為什么這么說呢,我們先分析下filterInvocationInterceptor(即org.acegisecurity.intercept.web.FilterSecurityInterceptor)的工作原理便可知道一二。

    FilterSecurityInterceptor的父類AbstractSecurityInterceptor中使用beforeInvocation方法對用戶訪問的資源進行抉擇,判斷用戶是否有訪問權限,這里主要是對URL進行判斷,在URL轉發之前判斷該用戶是否有訪問該URL的權限。這樣一來因為filterInvocationInterceptor對所有路徑進行過濾上面的(/**)設置,包括靜態圖片文件,css文件,flash文件等,這些url都要經過FilterSecurityInterceptor的判斷,這樣勢必影響頁面加載速度。那為什么登陸后系統會明顯變慢了,因為匿名用戶時,ACEGI讀取的匿名用戶的Resource列表為空,雖然也對所有的URL進行了權限判斷,但頁面加載并不顯得慢。經過上面的分析,嘗試了作了下面的修改,把每個Filter需要過濾的URL單獨寫,有公用的寫成一行,示例如下。作了這樣的優化,頁面加載的速度有了明顯的提升。

     

        <bean id="filterChainProxy"

              class="org.acegisecurity.util.FilterChainProxy">

            <property name="filterInvocationDefinitionSource">

                <value>

                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON

                    PATTERN_TYPE_APACHE_ANT                

    /j_acegi_security_check=httpSessionContextIntegrationFilter,captchaValidationProcessingFilter,

    authenticationProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor

                    /j_acegi_logout=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,

    anonymousProcessingFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,

    exceptionTranslationFilter,filterInvocationInterceptor

                    /**/*.html=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,anonymousProcessingFilter,

    basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor

                    /**/*.htm=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,anonymousProcessingFilter,

    basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor

                    /**/*.jsp=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,anonymousProcessingFilter,

    basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor

                    /**/*.do=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,anonymousProcessingFilter,

    basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor

                    /**/*.ajax=httpSessionContextIntegrationFilter,rememberMeProcessingFilter,anonymousProcessingFilter,

    basicProcessingFilter,securityContextHolderAwareRequestFilter,ajaxExceptionTranslationFilter,filterInvocationInterceptor

                </value>                  

            </property>

    </bean>

      value部分一行顯示不下,我手動折行了

     

    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1827059



    Acegi with Tapestry Updating...

    Filter類似于AOP的Round Device,可以在實際業務的前后加入自定義的操作。主要是通過Filter來過濾Request,然后加入Authentication、Authorization之類的安全過程。
        Acegi內置一些Filter,我們可以選擇需要的加入我的Web Application,這些Filter是有順序的,最好參照Acegi官方的reference
        Web Application使用Filter來截取請求,我們可以在web.xml 中的定義每一個需要的過濾器,但這樣比較麻煩,好一點的做法是在Web.xm中定義一個org.acegisecurity.util.FilterChainProxy,這個東西可以認為是個Filter鏈,在這個鏈中定義需要的所有Filters。
    從它名字FilterToBeanProxy大概也能猜到是個代理,實際代理Spring context里面類型為FilterChainProxy的target,FilterToBeanProxy會自己去尋找實現targetClass FilterChainProxy的Bean。
       
    web.xml中示范代碼:
     
    <!-- Acegi Channel Processing Filter -->
     <filter>
            <filter-name>Acegi Filter Chain Proxy</filter-name>
            <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
            <init-param>
                <param-name>targetClass</param-name>
                <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
            </init-param>
     </filter>
     <filter-mapping>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <url-pattern>/*</url-pattern>
     </filter-mapping>
    <!---->
     
    Sprnig appContext中示范代碼:
     
    <!-- filter Chain -->
    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
          <property name="filterInvocationDefinitionSource">
                  <value>
                       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                       PATTERN_TYPE_APACHE_ANT
                       /**= httpSessionContextIntegrationFilter,authenticationProcessingFilter,basicProcessingFilter,
                               exceptionTranslationFilter,filterInvocationInterceptor
                  </value>
         </property>
    </bean>

    <!-- -->
     其中
    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON: 比較前全部轉化為小寫,所以記得在下面的Pattern用小寫匹配。
    PATTERN_TYPE_APACHE_ANT:使用ANT類型的正則表達式,如果沒有這句話使用的是Perl樣式的正則表達式
    /**=后面的部分就是使用的一些Filters,他們都是Spring context中的Bean,下面我一一解釋。
     
    httpSessionContextIntegrationFilter
        每次request前 HttpSessionContextIntegrationFilter從Session中獲取Authentication對象,在request完后, 又把Authentication對象保存到Session中供下次request使用,此filter必須其他Acegi filter前使用。
     
    authenticationProcessingFilter
        起到認證管理的作用,它將驗證的功能委托給多個Provider,并通過遍歷Providers, 以保證獲取不同來源的身份認證,若某個Provider能
    成功確認當前用戶的身份,authenticate()方法會返回一個完整的包含用戶授權信息的Authentication對象,否則會拋出一個
    AuthenticationException。比如Provider可以是DaoAuthenticationProvider、Cas...、Jaas...等。
     
    basicProcessingFilter
        用于處理HTTP頭的認證信息,如從Spring遠程協議(如Hessian和Burlap)或普通的瀏覽器如IE,Navigator的HTTP頭中獲取用戶信息,將他們轉交給通過authenticationManager屬性裝配的認證管理器。如果認證成功,會將一個Authentication對象放到會話中,否則,如果認證失敗,會將控制轉交給認證入口點(通過authenticationEntryPoint屬性裝配)。
     
    exceptionTranslationFilter,filterInvocationInterceptor
        在Acegi1.0.0中是SecurityEnforcementFilter,Acegi1.0.0 RC2中將之拋棄,用以上兩個Filter來作為攔截器配合使用,并導向認證點。
     
    exceptionTranslationFilter
        它的屬性authenticationEntryPoint定義了拋出異常后定向到什么認證點。如果為authenticationProcessingFilterEntryPoint則為Form驗證,定向到一個login頁面由用戶輸入用戶名密碼,提交后請求會通過authenticationProcessingFilter去Provider驗證。如果為
    basicProcessingFilterEntryPoint則彈出一個對話框給用戶輸入用戶名密碼來驗證。
     
    filterInvocationInterceptor
        相當于一個攔截器,他的屬性objectDefinitionSource定義了什么樣的資源需要認證,他還有屬性authenticationManager和
    accessDecisionManager。
     
    channelProcessingFilter
        通道過濾器,他可以定義什么樣的request可以在Http下傳輸,什么樣的request可以在Https下傳輸,進行安全通道的轉換。這個過濾器一般緊跟著httpSessionContextIntegrationFilter
     

    下面我將用配置文件來詳細說明
    這一部分為比較common一些的配置

    authenticationManager
    authenticationManager是最核心的一個manager,很多filter中都要引用他來進行認證,他含有很多Provider,每個Provider負責具體的認證工作,比如下面的就是通過Dao來進行認證,還有通過Cas中央認證,Jaas認證等方式,這里不多說,查查文檔吧。
    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="daoAuthenticationProvider"/>
            </list>
        </property>
    </bean>
    daoAuthenticationProvider也有幾種實現方式,可以通過內存中的認證信息認證(不實用),也可以通過RDBMS信息來認證,還有一些,查查文檔吧!
    <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 name="userCache" ref="userCache"/
          -->
    </bean>
    saltSource定義了編碼密鑰,可選。
    passwordEncoder為編碼方式,有明文、MD5編碼、SHA編碼等,可選。
    userCache為緩存方式,可選。

    inMemoryDaoImpl即通過內存中的認證信息認證,如下。
    <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
      <property name="userMap">
        <value>
          ck=ck,ROLE_USER
        </value>
      </property>
    </bean>
    ck=ck,ROLE_USER表明其中一個用戶名為ck,密碼ck,角色是ROLE_USER。
    實際項目中一般是使用JdbcDaoImpl,去數據庫獲取認證信息,waiting for updating...
    accessDecisionManager waiting for updating...
     

    以下是filter配置
    <!-- httpSessionContextIntegrationFilter -->
    <bean id="httpSessionContextIntegrationFilter"
     class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
    <!-- -->
     
    <!-- ChannelProcessingFilter -->
    <bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter">
        <property name="channelDecisionManager">
              <ref bean="channelDecisionManager"/>
        </property>
        <property name="filterInvocationDefinitionSource">
             <value>
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                 \A.*storeroom.*\Z=REQUIRES_SECURE_CHANNEL
             </value>
        </property>
    </bean>
    其中的正則表達式規定了什么樣的URL請求需要轉成安全(Https)或非安全(Http)的請求
     
    <bean id="channelDecisionManager" class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl">
      <property name="channelProcessors">
        <list>
          <ref bean="secureChannelProcessor"/>
          <ref bean="insecureChannelProcessor"/>
        </list>
      </property>
    </bean>
    <bean id="secureChannelProcessor" class="org.acegisecurity.securechannel.SecureChannelProcessor"/>
    <bean id="insecureChannelProcessor" class="org.acegisecurity.securechannel.InsecureChannelProcessor"/>
    <!-- -->
     
    <!-- authenticationProcessingFilter -->
    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
      <property name="authenticationManager">
       <ref bean="authenticationManager"/>
      </property>
      <property name="filterProcessesUrl">
        <value>/j_acegi_security_check</value>
      </property>
      <property name="authenticationFailureUrl">
       <value>/app?service=page/MyLogin</value>
      </property>
      <property name="defaultTargetUrl">
       <value>/</value>
      </property>
    </bean>
    其中filterProcessesUrl是認證頁面輸入用戶名密碼的那個form的Action屬性的值
    <!-- -->
     
    <!-- basicProcessingFilter -->
    <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
          <property name="authenticationManager"><ref local="authenticationManager"/></property>
          <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
    </bean>
    <!-- -->

    <!-- EntryPoint -->
    <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
          <property name="realmName"><value>Daisy Realm</value></property>
    </bean>
     
    <bean id="authenticationProcessingFilterEntryPoint"  
    class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
        <property name="loginFormUrl"><value>/app?service=page/MyLogin</value></property>
        <property name="forceHttps"><value>false</value></property>
    </bean>
    其中loginFormUrl指明了導向到哪個認證頁面,由于是在Tapestry中,所以導向到頁面:
    上下文myapp/appservice=page/MyLogin, MyLogin是已經在tapestry application中定義了的頁面,注意URL的形式。
    <!-- -->
     
    <!-- exceptionTranslationFilter, filterInvocationInterceptor -->
    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint">
         <ref local="authenticationProcessingFilterEntryPoint"/>
        </property>
    </bean>
    其中的authenticationEntryPoint表明導向Form認證點還是導向Basic認證點
    <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
          <property name="authenticationManager">
            <ref bean="authenticationManager"/>
          </property>
          <property name="accessDecisionManager">
            <ref local="httpRequestAccessDecisionManager"/>
          </property>
          <property name="objectDefinitionSource">
             <value>
                 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                 \A.*storeroom.*\Z=ROLE_USER
             </value>
          </property>
    </bean>
    其中的objectDefinitionSource定義了什么樣的資源需要什么樣的角色。自我推測\A \Z表示正則表達式的開始和結束,.表示任意字符,*表示前面一個字符出現0到n次,所以\A.*storeroom.*\Z表示URL出現storeroom子串的請求,主要這里要全變為小寫才匹配。能執行這個請求得到結果必須具有權限ROLE_USER
    <!-- -->
     
    注1:認證form類似于
    <form name="logindata" method="post" action="j_acegi_security_check">
      <table>
        <tr>
          <td>id :</td>
          <td><input type="text" name="j_username" size="25" value="st001"></td>
        </tr>
        <tr>
          <td>password:</td>
          <td><input type="password" name="j_password" size="25" value="123456"/></td>
        </tr>
        <tr> 
          <td align="right" nowrap><input type="submit"  value="submit"></td>
          <td width="100%">&nbsp;</td>
        </tr>
      </table>
    </form>
     
    注2:關于用戶權限
    要分清兩種權限,一個是context中直接定義的ROLE_SHENTING, ROLE_xx 等,還有一個是在數據庫中定義的自定義權限編碼,
      a. 對與第一種:
      在jdbcDaoImpl中賦予用戶ROLE_SHENTING,
      在filterInvocationInterceptor的objectDefinitionSource中定義了各種URL所需的ROLE_XXX
      filterInvocationInterceptor的accessDecisionManager用RoleVoter來匹配以上的權限
      b.對與第二種:
      在context中基本不涉及,可以不用考慮,
     
      所以上面所有的授權全部是指的第一種,即定義在context中的ROLE_XXX
      JdbcDaoImpl中賦予權限例:
      <bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
          <property name="dataSource"><ref bean="dataSource"/></property>
          <property name="rolePrefix" value=""></property>
          <property name="usersByUsernameQuery"
           value="SELECT yhbh as username,ma as password,'10' as enabled FROM yh_user WHERE yhbh = ? and scbz = '0'"/>
          <property name="authoritiesByUsernameQuery"
            value="SELECT yhbh as username,'ROLE_SHENTIN' as authority FROM yh_user WHERE yhbh = ?"/>
       </bean>
       可見第一個sql驗證username和password的是否對應,第二個sql直接給了ROLE_的權限,和數據庫中自定義的權限編號無關
     

    summary:

     ---------------------------------------------------------------------------------------------------
    Normal Authentication
    1、filterInvocationInterceptor對請求進行攔截,objectDefinitionSource表示了URL的對應權限。
    2. filterInvocationInterceptor的authenticationManager和accessDecisionManager起作用,驗證身份,檢查權限
       如果符合條件就OK,否則拋出異常
    3. exceptionTranslationFilter攔截異常,重定向用戶到EntryPoint
    4. Form頁面,用戶輸入用戶名和密碼,點擊確認后Form的action為j_acegi_security_check
    5. 即請求中含"j_acegi_security_check"字符串,和authenticationProcessingFilter的filterProcessesUrl匹配,
    6. 于是此請求被authenticationProcessingFilter截取,送到authenticationProcessingFilter的authenticationManager
       驗證身份,如果通過就OK。
    ---------------------------------------------------------------------------------------------------
    ---------------------------------------------------------------------------------------------------
    CAS Authentication
    1. 同上,filterInvocationInterceptor對請求進行攔截,objectDefinitionSource表示了URL的對應權限。
    2. 同上,filterInvocationInterceptor的authenticationManager和accessDecisionManager起作用,驗證身份,檢查權限
       如果符合條件就OK,否則拋出異常
    3. 同上,exceptionTranslationFilter攔截異常,重定向用戶到EntryPoint
       casProcessingFilterEntryPoint的loginUrl為CAS服務器的驗證地址,serviceProperties為成功驗證后
       CAS服務器重定向的地址j_acegi_cas_security_check(相當于上面的action,但上面的尚未驗證,這里已驗證成功)
    4. casProcessingFilter的filterProcessesUrl和服務器身份驗證成功后的請求匹配(即含j_acegi_cas_security_check)
    5. 于是請求被casProcessingFilter獲取,在CAS服務器端通過身份驗證后,CAS會將之重定向到一個服務URL,在Acegi中
       還必須用authenticationManager的casAuthenticationProvider來處理Acegi這邊的身份驗證并檢查用戶權限。
    6. casAuthenticationProvider的casAuthoritiesPopulator去Dao檢查身份和權限
       casAuthenticationProvider的casProxyTicketValidator用來驗證當前請求的有效性
    7. casProxyTicketValidator定義了CAS端驗證票據的地址,以及成功驗證后CAS服務器重定向的地址j_acegi_cas_security_check
    ---------------------------------------------------------------------------------------------------

    Question:
    1.Normal Authentication中,是不是要在步驟2和4中驗證兩次



    轉載:Acegi 關鍵組件詳述-1

    1.Filter 組件

    HttpSessionContextIntegrationFilter

    該Filter負責每次請求前從HttpSession中獲取Authentication對象,然后把Authentication存于一個新的ContextHolder對象(其實質上只是一個ThreadLocal對象)中,則讓該次請求過程中的任何Filter都可以通過ContextHolder來共享Authentication,而不需要從HttpSession中取,減少傳HttpRequest參數的麻煩。在請求完后把Authentication對象保存到HttpSession中供下次請求使用, 最后把剛才生成的ContextHolder對象銷毀。這樣就達到了讓Authentication對象跨越多個請求的目的。

    注意此filter須在調用其他Acegi filter前使用:

    <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
    </bean>

          AuthenticationProcessingFilter

    該Filter負責處理登陸身份驗證。當接受到與filterProcessesUrl所定義相同的請求時,它會首先通過AuthenticationManager來驗證用戶身份。如果驗證成功,則重定向到defaultTargetUrl所定義的成功登陸頁面。如果驗證失敗,則再從rememberMeServices中獲取用戶身份,若再獲取失敗,則重定向到authenticationFailureUrl所定義登陸失敗頁面。

    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
          <property name="authenticationManager"><ref bean="authenticationManager"/></property>
          <property name="authenticationFailureUrl"><value>/acegilogin.jsp?login_error=1</value></property>
          <property name="defaultTargetUrl"><value>/</value></property>
          <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
          <property name="rememberMeServices"><ref local="rememberMeServices"/></property>
    </bean>

          LogoutFilter

    該Filter負責處理退出登錄后所需要的清理工作。它會把session銷毀,把ContextHolder清空, 把rememberMeServices從cookies中清除掉,然后重定向到指定的退出登陸頁面。

    <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
          <constructor-arg value="/index.jsp"/> <!-- URL redirected to after logout ->
          <constructor-arg>
             <list>
                  <ref bean="rememberMeServices"/>
                  <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
             </list>
          </constructor-arg>
    </bean>

          FilterInvocationInterceptor

    該過濾器會首先調用AuthenticationManager判斷用戶是否已登陸認證,如還沒認證成功,則重定向到登陸界面。認證成功,則并從Authentication中獲取用戶的權限。然后從objectDefinitionSource屬性獲取各種URL資源所對應的權限。最后調用AccessDecisionManager來判斷用戶所擁有的權限與當前受保華的URL資源所對應的權限是否相匹配。如果匹配失敗,則返回403錯誤(禁止訪問)給用戶。匹配成功則用戶可以訪問受保護的URL資源。

    <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
          <property name="authenticationManager"><ref bean="authenticationManager"/></property>
          <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
          <property name="objectDefinitionSource">
             <value>
                                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                                    PATTERN_TYPE_APACHE_ANT
                                    /index.jsp=ROLE_ANONYMOUS,ROLE_USER
                                    /hello.htm=ROLE_ANONYMOUS,ROLE_USER
                                    /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER
                                    /switchuser.jsp=ROLE_SUPERVISOR
                                    /j_acegi_switch_user=ROLE_SUPERVISOR
                                    /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
                                    /**=ROLE_USER
            </value>
          </property>
    </bean>

          SecurityContextHolderAwareRequestFilter

    該Filter負責通過Decorate Model(裝飾模式),裝飾的HttpServletRequest對象。其Wapper是ServletRequest包裝類HttpServletRequestWrapper的子類(SavedRequestAwareWrapper或SecurityContextHolderAwareRequestWrapper),附上獲取用戶權限信息,request參數,headers, Date headers 和 cookies 的方法。

    <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter" />

          BasicProcessingFilter

    該Filter負責處理HTTP頭的認證信息,如從Spring遠程協議(如Hessian和Burlap)或普通的瀏覽器如IE,Navigator的HTTP頭中獲取用戶信息,將他們轉交給通過authenticationManager屬性裝配的認證管理器。如果認證成功,會將一個Authentication對象放到會話中,否則,如果認證失敗,會將控制轉交給認證入口點(通過authenticationEntryPoint屬性裝配)

    <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
          <property name="authenticationManager"><ref local="authenticationManager"/></property>
          <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
       </bean>

       <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
          <property name="realmName"><value>Contacts Realm</value></property>
       </bean>

          RememberMeProcessingFilter

    該Filter負責在用戶登錄后在本地機上記錄用戶cookies信息,免除下次再次登陸。檢查AuthenticationManager 中是否已存在Authentication對象,如果不存在則會調用RememberMeServices的aotoLogin方法來從cookies中獲取Authentication對象

        <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
                       <property name="authenticationManager" ref="authenticationManager" />
                       <property name="rememberMeServices" ref="rememberMeServices" />
        </bean>

          AnonymousProcessingFilter

    該Filter負責為當不存在任何授權信息時,自動為Authentication對象添加userAttribute中定義的匿名用戶權限

        <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
             <property name="key" value="changeThis" />
             <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" />
        </bean>

          ExceptionTranslationFilter

    該過濾器負責處理各種異常,然后重定向到相應的頁面中。

             <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
                       <property name="authenticationEntryPoint">
                                <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
                                         <property name="loginFormUrl" value="/login.jsp" />
                                         <property name="forceHttps" value="false" />
                                </bean>
                       </property>
                       <property name="accessDeniedHandler">
                                <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
                                         <property name="errorPage" value="/accessDenied.jsp" />
                                </bean>
                       </property>
             </bean>




    Acegi學習小結


    一、基本原理
    Acegi認證授權主要基于兩大技術,一是Filter機制,二是AOP的攔截機制。通過FilterSecurityInterceptor很好地實現了對URI的保護,通過MethodSecurityInterceptor實現了對Service的方法的攔截保護,通過ACL 實現了對prototype類型的Object進行過濾和保護。

    二、基本概念
    HttpSessionContextIntegrationFilter 存儲SecurityContext in HttpSession
    ChannelProcessingFilter 重定向到另一種協議,如http到https

    ConcurrentSessionFilter 因為不使用任何SecurityContextHolder的功能,但是需要更新SessionRegistry來表示當前的發送請求的principal,通過在web.xml中注冊Listener監聽Session事件,并發布相關消息,然后由SessionRegistry獲得消息以判斷當前用戶的Session數量。

    AuthenticationProcessingFilter 普通認證機制(大多數用這個)

    CasProcessingFilter CAS認證機制

    BasicProcessingFilter Http協議的Basic認證機制

    HttpRequestIntegrationFilter Authentication 從容器的HttpServletRequest.getUserPrincipal()獲得

    JbossIntegrationFilter 與Jboss相關。

    SecurityContextHolderAwareRequestFilter 與servlet容器結合使用。

    RememberMeProcessingFilter 基于Cookies方式進行認證。

    AnonymousProcessingFilter 匿名認證。

    ExceptionTranslationFilter 捕獲所有的Acegi Security 異常,這樣要么返回一個HTTP錯誤響應或者加載一個對應的AuthenticationEntryPoint

    AuthenticationEntryPoint 認證入口

    三、Acegi認證授權流程
    1、FilterToBeanProxy 負責代理請求給FilterChainProxy

    2、FilterChainProxy 方便的將多個Filter串聯起來,如上面基本概念中提到的各種Filter,當然如果對URI進行授權保護,也可以包含FilterSecurityInterceptor。注意各Filter的順序。

    3、AbstractSecurityInterceptor 調度中心。負責調用各模塊完成相應功能。
    FilterSecurityInterceptor 對URI進行攔截保護
    AspectJSecurityInterceptor 對方法進行攔截保護
    MethodSecurityInterceptor 對方法進行攔截保護

    4、AuthenticationManager 用戶認證
    -> AuthenticationProvider 實際進行用戶認證的地方(多個)。
    -> UserDetailsService 返回帶有GrantedAuthority的UserDetail或者拋出異常。

    5、AccessDecisionManager(UnanimousBased/AffirmativeBased/ConsensusBased) 授權
    -> AccessDecisionVoter(RoleVoter/BaseAclEntryVoter) 實際投票的Voter(多個).

    6、RunAsManager 變更GrantedAuthority

    7、AfterInvocationManager 變更返回的對象
    -> BaseInvocationProvider 實際完成返回對象變更的地方(多個)。

    四、Acegi實例

    http://www.javaeye.com/topic/43341




    再論 Acegi 權限存儲策略



    本文出處
    http://starcraft.blogdriver.com/starcraft/1135045.html

    在我之前的一篇文章里, 說明了在 Acegi 中如何將資源權限數據存儲到數據庫中, 文章見 http://www.hibernate.org.cn/viewtopic.php?t=17538, 雖然文中方式實現了從數據庫讀取資源權限, 但是代碼量較大, 并且重載了 SecurityEnforcementFilter, 造成比較大的侵入性, 這里我將提供另一種更簡潔的方式實現此功能.

    入口還是 org.acegisecurity.intercept.web.FilterSecurityInterceptor, 資源權限配置來自于 objectDefinitionSource, 標準配置方式采用 FilterInvocationDefinitionSourceEditor 解析, 支持 Perl5 和 AntPath 兩種風格, 為了實現從其它位置(典型如數據庫), 我們要做的就是實現一個自定義的 FilterInvocationDefinitionSource, 查看類層次結構圖后可以發現, acegi 中已經有一個 AbstractFilterInvocationDefinitionSource 已經實現此接口, 只要實現一個抽象方法即可

     public abstract ConfigAttributeDefinition lookupAttributes(String url);

    因此, 自定義一個 Class 如下 :

    public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource

    因為 acegi 中已經提供了 Perl5 和 AntPath 的實現, 只需要集成過來即可, 因此定義接口如下

    /**
     * <class>ConfigableFilterInvocationDefinition</class> 支持 Perl5 和 ant Path 兩種風格的資源配置方式
     * @since 2006-1-19
     * @author 王政
     * @version $Id: ConfigableFilterInvocationDefinition.java,v 1.3 2006/01/19 09:40:37 wz Exp $
     */
    public interface ConfigableFilterInvocationDefinition {
     
     /** The Perl5 expression  */
     String PERL5_KEY = "PATTERN_TYPE_PERL5";
       
        /** The ant path expression */
        String ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT";
     
        /** 標準分隔符 */
        String STAND_DELIM_CHARACTER = ",";
       
        /**
         * Set resource expression, the value must be
    {@link #PERL5_KEY_REG_EXP} or {@link #ANT_PATH_KEY}
         * @see #REOURCE_EXPRESSION_PERL5_REG_EXP
         * @see #RESOURCE_EXPRESSION_ANT_PATH_KEY
         * @param resourceExpression the resource expression
         */
        void setResourceExpression(String resourceExpression);
       
        /**
         *
         * @return resource expression
         */
        String getResourceExpression();
       
        /**
         * Set whether convert url to lowercase before comparison
         * @param convertUrlToLowercaseBeforeComparison whether convertUrlToLowercaseBeforeComparison
         */
        void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison);
     
        /**
         *
         * @return whether convert url to lowercase before comparison
         */
        boolean isConvertUrlToLowercaseBeforeComparison();
      
    }

     再讓 RdbmsBasedFilterInvocationDefinitionSource 實現此接口即可, 下面是簡略代碼 

    /**
     * <class>RdbmsBasedFilterInvocationDefinitionSource</class> 是基于數據庫的權限存儲實現, 它支持兩種風格的配置
     * @see com.skyon.uum.security.acegi.intercept.web.ConfigableFilterInvocationDefinition
     * @see org.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap
     * @see org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap
     * @since 2006-1-19
     * @author 王政
     * @version $Id: RdbmsBasedFilterInvocationDefinitionSource.java,v 1.6 2006/02/13 03:20:55 wz Exp $
     */
    public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource
     implements ConfigableFilterInvocationDefinition, InitializingBean {
     
     
        //~ Static fields/initializers =============================================

        private static final Log logger = LogFactory.getLog(RdbmsBasedFilterInvocationDefinitionSource.class);
     
        // ~ Instance fields ========================================================
     
        private String resourceExpression = PERL5_KEY;
       
        private boolean convertUrlToLowercaseBeforeComparison = false;
        
        private ResourceMappingProvider resourceMappingProvider;
       
        //  ~ Methods ================================================================
       
     /**
      *
      * @see org.acegisecurity.intercept.web.AbstractFilterInvocationDefinitionSource#lookupAttributes(java.lang.String)
      */
     public ConfigAttributeDefinition lookupAttributes(String url) {
      FilterInvocationDefinitionSource actualSource = populateFilterInvocationDefinitionSource();
      
      if (RegExpBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {
       return ((RegExpBasedFilterInvocationDefinitionMap) actualSource).lookupAttributes(url);
      } else if (PathBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {
       return ((PathBasedFilterInvocationDefinitionMap) actualSource).lookupAttributes(url);
      }
      
      throw new IllegalStateException("wrong type of " + actualSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class
        + " or " + PathBasedFilterInvocationDefinitionMap.class);  
     }
     
     /**
      *
      * @see org.acegisecurity.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
      */
     public Iterator getConfigAttributeDefinitions() {
      FilterInvocationDefinitionSource actualSource = populateFilterInvocationDefinitionSource();
      
      if (RegExpBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {
       return ((RegExpBasedFilterInvocationDefinitionMap) actualSource).getConfigAttributeDefinitions();
      } else if (PathBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {
       return ((PathBasedFilterInvocationDefinitionMap) actualSource).getConfigAttributeDefinitions();
      }
      
      throw new IllegalStateException("wrong type of " + actualSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class
        + " or " + PathBasedFilterInvocationDefinitionMap.class); 
     }
     
     
        private FilterInvocationDefinitionSource populateFilterInvocationDefinitionSource() {             
         FilterInvocationDefinitionMap definitionSource = null;
         
         if (PERL5_KEY.equals(getResourceExpression())) {
          definitionSource = new RegExpBasedFilterInvocationDefinitionMap();
         } else if (ANT_PATH_KEY.equals(getResourceExpression())) {
          definitionSource = new PathBasedFilterInvocationDefinitionMap();
         } else {
          throw new IllegalArgumentException("wrong resourceExpression value");
         }
           
            definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison());
           
            ResourceMapping[] mappings = getResourceMappingProvider().getResourceMappings();
            if (mappings == null || mappings.length ==0) {
                return (FilterInvocationDefinitionSource) definitionSource;
            }
           
            for (int i = 0; i < mappings.length; i++) {
                ResourceMapping mapping = mappings[i];
                String[] recipents = mapping.getRecipients();
               
                if (recipents == null || recipents.length == 0) {
                    if (logger.isErrorEnabled()) {
                        logger.error("Notice, the resource : " + mapping.getResourcePath() + " hasn't no recipents, it will access by any one ! ");
                    }
                    continue;
                }
               
                StringBuffer valueBuffer = new StringBuffer();
                for (int j = 0; j < recipents.length; j++) {
                    valueBuffer.append(recipents[j]);
                    if (j < recipents.length - 1) {
                        valueBuffer.append(STAND_DELIM_CHARACTER);
                    }
                }
                String value = valueBuffer.toString();                   
                addSecureUrl(definitionSource, mapping.getResourcePath(), value);
             }
        
            return (FilterInvocationDefinitionSource )definitionSource;
        }
     
       
        /**
         * @param source
         * @param name
         * @param value
         * @throws IllegalArgumentException
         */
        private synchronized void addSecureUrl(FilterInvocationDefinitionMap source, String name, String value)
            throws IllegalArgumentException {
           
            // Convert value to series of security configuration attributes
            ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();
            configAttribEd.setAsText(value);

            ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd.getValue();

            // Register the regular expression and its attribute
            source.addSecureUrl(name, attr);
        }
       
        省去 getter, setter....

    }


    ResourceMappingProvider

    public interface ResourceMappingProvider {
       
     String RESOURCE_PATH_PREFIX = "/";
     
        /**
         * Get Resource Mapping
         * @return resource mapping
         */
        ResourceMapping[] getResourceMappings();
       
    }


    public class ResourceMapping {
       
        // url
        private String resourcePath;
       
        // 即角色
        private String[] recipients = new String[0];
          
        省去 getter, setter....

    這樣就很完美的既支持了從數據庫的讀取數據, 又可以自由選擇 Perl5 和 AntPath 兩種風格的配置, 最后不要忘了給 ResourceMappingProvider 加一層 cache, 我的配置如下

    <bean id="resourceCache" class="com.skyon.uum.security.acegi.intercept.web.cache.EhCacheBasedResourceCache">
      <property name="dataSource">
       <ref bean="dataSource"></ref>
      </property>
      <property name="cache">
       <bean parent="cacheTemplate">    
        <property name="cacheName"><value>resourceCache</value></property>
       </bean>
            </property>
      <property name="allResourcesQuery">
       <value>
       select distinct t.id, t.id, t.parent_id, t.url, t.title, t.layer, t.type, t.application_id
       from uum_resource t order by t.orderField
       </value>
      </property>
     </bean>
     
     <bean id="permissionCache" class="com.skyon.uum.security.acegi.intercept.web.cache.EhCacheBasedPermissionCache">
      <property name="dataSource">
       <ref bean="dataSource"></ref>
      </property>
      <property name="cache">
       <bean parent="cacheTemplate">    
        <property name="cacheName"><value>permissionCache</value></property>
       </bean>
            </property>
      <property name="recipentsResourceMappingQuery">
       <value>
       select r.name, p.resource_id from uum_permission p left outer join uum_role r on p.role_id = r.id
       </value>
      </property> 
     </bean>
     
       
     <bean id="resourceMappingProvider" class="com.skyon.uum.security.acegi.intercept.web.ResourceMappingProviderImpl" autowire="byType"/>

        <!-- ======================== AUTHENTICATION ======================= -->
       
        <!-- Note the order that entries are placed against the objectDefinitionSource is critical.
             The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
             Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last -->
        <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
            <property name="authenticationManager"><ref local="authenticationManager"/></property>
            <property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
            <property name="objectDefinitionSource"><ref local="filterInvocationDefinitionSource"></ref></property>
      <!--
      <property name="rejectPublicInvocations"><value>true</value></property>
      -->
        </bean>
     
     <bean id="filterInvocationDefinitionSource" class="com.skyon.uum.security.acegi.intercept.web.RdbmsBasedFilterInvocationDefinitionSource">
      <property name="resourceMappingProvider">
       <ref local="resourceMappingProvider"></ref>
      </property>
      <property name="resourceExpression">
       <value>PATTERN_TYPE_APACHE_ANT</value>
      </property>
     </bean>

     這段時間看了很多人對 Acegi 的評價, 有不少觀點認為 Acegi 的配置太過繁瑣, 其實權限控制本來就不是一件很輕松的事, Acegi 用 AOP 實現, 配置文件的確有些繁瑣, 但是只要一個配置文件就解決了整個系統的權限問題, 可謂一勞永逸, 相比較在 Action 中實現應該還是利遠大于弊, 也有人說用 WebWork 的 Interceptor 實現, 雖然也是不錯的 solution, 但是不要忘了, 并不是所有的項目都使用 webwork , 假如有一個 struts 的項目, 權限控制就會有移植性的問題.

    我的 msn : shartcn@msn.com, 有問題歡迎討論

    posted on 2007-12-25 09:35 蘆葦 閱讀(1876) 評論(0)  編輯  收藏 所屬分類: Spring
    主站蜘蛛池模板: 亚洲国产一成人久久精品| 在线免费观看毛片网站| 亚洲综合国产精品第一页| 亚洲AV无码专区在线观看成人 | 国产亚洲精品高清在线| 国产亚洲Av综合人人澡精品| 国产成人精品免费视频大全五级| 亚洲不卡影院午夜在线观看| 亚洲最大免费视频网| 337p日本欧洲亚洲大胆精品555588 | 亚洲国产精品一区二区成人片国内| 两个人看的www视频免费完整版| 久久亚洲国产精品123区| 福利免费在线观看| 亚洲国产成人片在线观看| 亚洲一区二区无码偷拍| 两个人看的www免费高清| 亚洲色欲色欲综合网站| 免费一本色道久久一区| 亚洲国产欧美日韩精品一区二区三区| 日本一道本高清免费| 一区二区三区免费电影| 亚洲a在线视频视频| 在线免费视频一区| 人妻18毛片a级毛片免费看| 亚洲AV无码码潮喷在线观看| 国产在线a免费观看| 白白色免费在线视频| 久久综合图区亚洲综合图区| 99久久99久久精品免费看蜜桃 | 亚洲AV无码成人网站久久精品大| 伊人久久免费视频| 亚洲另类无码专区丝袜| 久久精品国产亚洲7777| 18禁成人网站免费观看| 免费播放美女一级毛片| 一区二区三区亚洲| 免费人成视频在线观看视频| 最近2019中文字幕免费直播 | 亚洲精品国产精品乱码在线观看| **一级一级毛片免费观看|