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

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

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

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
    對(duì)ACEGI中FilterChainProxy進(jìn)行性能調(diào)優(yōu)

    一般情況下,在ACEGI中隊(duì)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>

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

    FilterSecurityInterceptor的父類AbstractSecurityInterceptor中使用beforeInvocation方法對(duì)用戶訪問的資源進(jìn)行抉擇,判斷用戶是否有訪問權(quán)限,這里主要是對(duì)URL進(jìn)行判斷,在URL轉(zhuǎn)發(fā)之前判斷該用戶是否有訪問該URL的權(quán)限。這樣一來因?yàn)?/span>filterInvocationInterceptor對(duì)所有路徑進(jìn)行過濾上面的(/**)設(shè)置,包括靜態(tài)圖片文件,css文件,flash文件等,這些url都要經(jīng)過FilterSecurityInterceptor的判斷,這樣勢(shì)必影響頁面加載速度。那為什么登陸后系統(tǒng)會(huì)明顯變慢了,因?yàn)槟涿脩魰r(shí),ACEGI讀取的匿名用戶的Resource列表為空,雖然也對(duì)所有的URL進(jìn)行了權(quán)限判斷,但頁面加載并不顯得慢。經(jīng)過上面的分析,嘗試了作了下面的修改,把每個(gè)Filter需要過濾的URL單獨(dú)寫,有公用的寫成一行,示例如下。作了這樣的優(yōu)化,頁面加載的速度有了明顯的提升。

     

        <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部分一行顯示不下,我手動(dòng)折行了

     

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



    Acegi with Tapestry Updating...

    Filter類似于AOP的Round Device,可以在實(shí)際業(yè)務(wù)的前后加入自定義的操作。主要是通過Filter來過濾Request,然后加入Authentication、Authorization之類的安全過程。
        Acegi內(nèi)置一些Filter,我們可以選擇需要的加入我的Web Application,這些Filter是有順序的,最好參照Acegi官方的reference
        Web Application使用Filter來截取請(qǐng)求,我們可以在web.xml 中的定義每一個(gè)需要的過濾器,但這樣比較麻煩,好一點(diǎn)的做法是在Web.xm中定義一個(gè)org.acegisecurity.util.FilterChainProxy,這個(gè)東西可以認(rèn)為是個(gè)Filter鏈,在這個(gè)鏈中定義需要的所有Filters。
    從它名字FilterToBeanProxy大概也能猜到是個(gè)代理,實(shí)際代理Spring context里面類型為FilterChainProxy的target,FilterToBeanProxy會(huì)自己去尋找實(shí)現(xiàn)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: 比較前全部轉(zhuǎn)化為小寫,所以記得在下面的Pattern用小寫匹配。
    PATTERN_TYPE_APACHE_ANT:使用ANT類型的正則表達(dá)式,如果沒有這句話使用的是Perl樣式的正則表達(dá)式
    /**=后面的部分就是使用的一些Filters,他們都是Spring context中的Bean,下面我一一解釋。
     
    httpSessionContextIntegrationFilter
        每次request前 HttpSessionContextIntegrationFilter從Session中獲取Authentication對(duì)象,在request完后, 又把Authentication對(duì)象保存到Session中供下次request使用,此filter必須其他Acegi filter前使用。
     
    authenticationProcessingFilter
        起到認(rèn)證管理的作用,它將驗(yàn)證的功能委托給多個(gè)Provider,并通過遍歷Providers, 以保證獲取不同來源的身份認(rèn)證,若某個(gè)Provider能
    成功確認(rèn)當(dāng)前用戶的身份,authenticate()方法會(huì)返回一個(gè)完整的包含用戶授權(quán)信息的Authentication對(duì)象,否則會(huì)拋出一個(gè)
    AuthenticationException。比如Provider可以是DaoAuthenticationProvider、Cas...、Jaas...等。
     
    basicProcessingFilter
        用于處理HTTP頭的認(rèn)證信息,如從Spring遠(yuǎn)程協(xié)議(如Hessian和Burlap)或普通的瀏覽器如IE,Navigator的HTTP頭中獲取用戶信息,將他們轉(zhuǎn)交給通過authenticationManager屬性裝配的認(rèn)證管理器。如果認(rèn)證成功,會(huì)將一個(gè)Authentication對(duì)象放到會(huì)話中,否則,如果認(rèn)證失敗,會(huì)將控制轉(zhuǎn)交給認(rèn)證入口點(diǎn)(通過authenticationEntryPoint屬性裝配)。
     
    exceptionTranslationFilter,filterInvocationInterceptor
        在Acegi1.0.0中是SecurityEnforcementFilter,Acegi1.0.0 RC2中將之拋棄,用以上兩個(gè)Filter來作為攔截器配合使用,并導(dǎo)向認(rèn)證點(diǎn)。
     
    exceptionTranslationFilter
        它的屬性authenticationEntryPoint定義了拋出異常后定向到什么認(rèn)證點(diǎn)。如果為authenticationProcessingFilterEntryPoint則為Form驗(yàn)證,定向到一個(gè)login頁面由用戶輸入用戶名密碼,提交后請(qǐng)求會(huì)通過authenticationProcessingFilter去Provider驗(yàn)證。如果為
    basicProcessingFilterEntryPoint則彈出一個(gè)對(duì)話框給用戶輸入用戶名密碼來驗(yàn)證。
     
    filterInvocationInterceptor
        相當(dāng)于一個(gè)攔截器,他的屬性objectDefinitionSource定義了什么樣的資源需要認(rèn)證,他還有屬性authenticationManager和
    accessDecisionManager。
     
    channelProcessingFilter
        通道過濾器,他可以定義什么樣的request可以在Http下傳輸,什么樣的request可以在Https下傳輸,進(jìn)行安全通道的轉(zhuǎn)換。這個(gè)過濾器一般緊跟著httpSessionContextIntegrationFilter
     

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

    authenticationManager
    authenticationManager是最核心的一個(gè)manager,很多filter中都要引用他來進(jìn)行認(rèn)證,他含有很多Provider,每個(gè)Provider負(fù)責(zé)具體的認(rèn)證工作,比如下面的就是通過Dao來進(jìn)行認(rèn)證,還有通過Cas中央認(rèn)證,Jaas認(rèn)證等方式,這里不多說,查查文檔吧。
    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="daoAuthenticationProvider"/>
            </list>
        </property>
    </bean>
    daoAuthenticationProvider也有幾種實(shí)現(xiàn)方式,可以通過內(nèi)存中的認(rèn)證信息認(rèn)證(不實(shí)用),也可以通過RDBMS信息來認(rèn)證,還有一些,查查文檔吧!
    <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即通過內(nèi)存中的認(rèn)證信息認(rèn)證,如下。
    <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
      <property name="userMap">
        <value>
          ck=ck,ROLE_USER
        </value>
      </property>
    </bean>
    ck=ck,ROLE_USER表明其中一個(gè)用戶名為ck,密碼ck,角色是ROLE_USER。
    實(shí)際項(xiàng)目中一般是使用JdbcDaoImpl,去數(shù)據(jù)庫獲取認(rèn)證信息,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>
    其中的正則表達(dá)式規(guī)定了什么樣的URL請(qǐng)求需要轉(zhuǎn)成安全(Https)或非安全(Http)的請(qǐng)求
     
    <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是認(rèn)證頁面輸入用戶名密碼的那個(gè)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>
    其中l(wèi)oginFormUrl指明了導(dǎo)向到哪個(gè)認(rèn)證頁面,由于是在Tapestry中,所以導(dǎo)向到頁面:
    上下文myapp/appservice=page/MyLogin, MyLogin是已經(jīng)在tapestry application中定義了的頁面,注意URL的形式。
    <!-- -->
     
    <!-- exceptionTranslationFilter, filterInvocationInterceptor -->
    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
        <property name="authenticationEntryPoint">
         <ref local="authenticationProcessingFilterEntryPoint"/>
        </property>
    </bean>
    其中的authenticationEntryPoint表明導(dǎo)向Form認(rèn)證點(diǎn)還是導(dǎo)向Basic認(rèn)證點(diǎn)
    <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定義了什么樣的資源需要什么樣的角色。自我推測(cè)\A \Z表示正則表達(dá)式的開始和結(jié)束,.表示任意字符,*表示前面一個(gè)字符出現(xiàn)0到n次,所以\A.*storeroom.*\Z表示URL出現(xiàn)storeroom子串的請(qǐng)求,主要這里要全變?yōu)樾懖牌ヅ洹D軋?zhí)行這個(gè)請(qǐng)求得到結(jié)果必須具有權(quán)限ROLE_USER
    <!-- -->
     
    注1:認(rèn)證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:關(guān)于用戶權(quán)限
    要分清兩種權(quán)限,一個(gè)是context中直接定義的ROLE_SHENTING, ROLE_xx 等,還有一個(gè)是在數(shù)據(jù)庫中定義的自定義權(quán)限編碼,
      a. 對(duì)與第一種:
      在jdbcDaoImpl中賦予用戶ROLE_SHENTING,
      在filterInvocationInterceptor的objectDefinitionSource中定義了各種URL所需的ROLE_XXX
      filterInvocationInterceptor的accessDecisionManager用RoleVoter來匹配以上的權(quán)限
      b.對(duì)與第二種:
      在context中基本不涉及,可以不用考慮,
     
      所以上面所有的授權(quán)全部是指的第一種,即定義在context中的ROLE_XXX
      JdbcDaoImpl中賦予權(quán)限例:
      <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>
       可見第一個(gè)sql驗(yàn)證username和password的是否對(duì)應(yīng),第二個(gè)sql直接給了ROLE_的權(quán)限,和數(shù)據(jù)庫中自定義的權(quán)限編號(hào)無關(guān)
     

    summary:

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

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



    轉(zhuǎn)載:Acegi 關(guān)鍵組件詳述-1

    1.Filter 組件

    HttpSessionContextIntegrationFilter

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

    注意此filter須在調(diào)用其他Acegi filter前使用:

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

          AuthenticationProcessingFilter

    該Filter負(fù)責(zé)處理登陸身份驗(yàn)證。當(dāng)接受到與filterProcessesUrl所定義相同的請(qǐng)求時(shí),它會(huì)首先通過AuthenticationManager來驗(yàn)證用戶身份。如果驗(yàn)證成功,則重定向到defaultTargetUrl所定義的成功登陸頁面。如果驗(yàn)證失敗,則再從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負(fù)責(zé)處理退出登錄后所需要的清理工作。它會(huì)把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

    該過濾器會(huì)首先調(diào)用AuthenticationManager判斷用戶是否已登陸認(rèn)證,如還沒認(rèn)證成功,則重定向到登陸界面。認(rèn)證成功,則并從Authentication中獲取用戶的權(quán)限。然后從objectDefinitionSource屬性獲取各種URL資源所對(duì)應(yīng)的權(quán)限。最后調(diào)用AccessDecisionManager來判斷用戶所擁有的權(quán)限與當(dāng)前受保華的URL資源所對(duì)應(yīng)的權(quán)限是否相匹配。如果匹配失敗,則返回403錯(cuò)誤(禁止訪問)給用戶。匹配成功則用戶可以訪問受保護(hù)的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負(fù)責(zé)通過Decorate Model(裝飾模式),裝飾的HttpServletRequest對(duì)象。其Wapper是ServletRequest包裝類HttpServletRequestWrapper的子類(SavedRequestAwareWrapper或SecurityContextHolderAwareRequestWrapper),附上獲取用戶權(quán)限信息,request參數(shù),headers, Date headers 和 cookies 的方法。

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

          BasicProcessingFilter

    該Filter負(fù)責(zé)處理HTTP頭的認(rèn)證信息,如從Spring遠(yuǎn)程協(xié)議(如Hessian和Burlap)或普通的瀏覽器如IE,Navigator的HTTP頭中獲取用戶信息,將他們轉(zhuǎn)交給通過authenticationManager屬性裝配的認(rèn)證管理器。如果認(rèn)證成功,會(huì)將一個(gè)Authentication對(duì)象放到會(huì)話中,否則,如果認(rèn)證失敗,會(huì)將控制轉(zhuǎn)交給認(rèn)證入口點(diǎn)(通過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負(fù)責(zé)在用戶登錄后在本地機(jī)上記錄用戶cookies信息,免除下次再次登陸。檢查AuthenticationManager 中是否已存在Authentication對(duì)象,如果不存在則會(huì)調(diào)用RememberMeServices的aotoLogin方法來從cookies中獲取Authentication對(duì)象

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

          AnonymousProcessingFilter

    該Filter負(fù)責(zé)為當(dāng)不存在任何授權(quán)信息時(shí),自動(dòng)為Authentication對(duì)象添加userAttribute中定義的匿名用戶權(quán)限

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

          ExceptionTranslationFilter

    該過濾器負(fù)責(zé)處理各種異常,然后重定向到相應(yīng)的頁面中。

             <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學(xué)習(xí)小結(jié)


    一、基本原理
    Acegi認(rèn)證授權(quán)主要基于兩大技術(shù),一是Filter機(jī)制,二是AOP的攔截機(jī)制。通過FilterSecurityInterceptor很好地實(shí)現(xiàn)了對(duì)URI的保護(hù),通過MethodSecurityInterceptor實(shí)現(xiàn)了對(duì)Service的方法的攔截保護(hù),通過ACL 實(shí)現(xiàn)了對(duì)prototype類型的Object進(jìn)行過濾和保護(hù)。

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

    ConcurrentSessionFilter 因?yàn)椴皇褂萌魏蜸ecurityContextHolder的功能,但是需要更新SessionRegistry來表示當(dāng)前的發(fā)送請(qǐng)求的principal,通過在web.xml中注冊(cè)Listener監(jiān)聽Session事件,并發(fā)布相關(guān)消息,然后由SessionRegistry獲得消息以判斷當(dāng)前用戶的Session數(shù)量。

    AuthenticationProcessingFilter 普通認(rèn)證機(jī)制(大多數(shù)用這個(gè))

    CasProcessingFilter CAS認(rèn)證機(jī)制

    BasicProcessingFilter Http協(xié)議的Basic認(rèn)證機(jī)制

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

    JbossIntegrationFilter 與Jboss相關(guān)。

    SecurityContextHolderAwareRequestFilter 與servlet容器結(jié)合使用。

    RememberMeProcessingFilter 基于Cookies方式進(jìn)行認(rèn)證。

    AnonymousProcessingFilter 匿名認(rèn)證。

    ExceptionTranslationFilter 捕獲所有的Acegi Security 異常,這樣要么返回一個(gè)HTTP錯(cuò)誤響應(yīng)或者加載一個(gè)對(duì)應(yīng)的AuthenticationEntryPoint

    AuthenticationEntryPoint 認(rèn)證入口

    三、Acegi認(rèn)證授權(quán)流程
    1、FilterToBeanProxy 負(fù)責(zé)代理請(qǐng)求給FilterChainProxy

    2、FilterChainProxy 方便的將多個(gè)Filter串聯(lián)起來,如上面基本概念中提到的各種Filter,當(dāng)然如果對(duì)URI進(jìn)行授權(quán)保護(hù),也可以包含F(xiàn)ilterSecurityInterceptor。注意各Filter的順序。

    3、AbstractSecurityInterceptor 調(diào)度中心。負(fù)責(zé)調(diào)用各模塊完成相應(yīng)功能。
    FilterSecurityInterceptor 對(duì)URI進(jìn)行攔截保護(hù)
    AspectJSecurityInterceptor 對(duì)方法進(jìn)行攔截保護(hù)
    MethodSecurityInterceptor 對(duì)方法進(jìn)行攔截保護(hù)

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

    5、AccessDecisionManager(UnanimousBased/AffirmativeBased/ConsensusBased) 授權(quán)
    -> AccessDecisionVoter(RoleVoter/BaseAclEntryVoter) 實(shí)際投票的Voter(多個(gè)).

    6、RunAsManager 變更GrantedAuthority

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

    四、Acegi實(shí)例

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




    再論 Acegi 權(quán)限存儲(chǔ)策略



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

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

    入口還是 org.acegisecurity.intercept.web.FilterSecurityInterceptor, 資源權(quán)限配置來自于 objectDefinitionSource, 標(biāo)準(zhǔn)配置方式采用 FilterInvocationDefinitionSourceEditor 解析, 支持 Perl5 和 AntPath 兩種風(fēng)格, 為了實(shí)現(xiàn)從其它位置(典型如數(shù)據(jù)庫), 我們要做的就是實(shí)現(xiàn)一個(gè)自定義的 FilterInvocationDefinitionSource, 查看類層次結(jié)構(gòu)圖后可以發(fā)現(xiàn), acegi 中已經(jīng)有一個(gè) AbstractFilterInvocationDefinitionSource 已經(jīng)實(shí)現(xiàn)此接口, 只要實(shí)現(xiàn)一個(gè)抽象方法即可

     public abstract ConfigAttributeDefinition lookupAttributes(String url);

    因此, 自定義一個(gè) Class 如下 :

    public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource

    因?yàn)?acegi 中已經(jīng)提供了 Perl5 和 AntPath 的實(shí)現(xiàn), 只需要集成過來即可, 因此定義接口如下

    /**
     * <class>ConfigableFilterInvocationDefinition</class> 支持 Perl5 和 ant Path 兩種風(fēng)格的資源配置方式
     * @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";
     
        /** 標(biāo)準(zhǔn)分隔符 */
        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 實(shí)現(xiàn)此接口即可, 下面是簡略代碼 

    /**
     * <class>RdbmsBasedFilterInvocationDefinitionSource</class> 是基于數(shù)據(jù)庫的權(quán)限存儲(chǔ)實(shí)現(xiàn), 它支持兩種風(fēng)格的配置
     * @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....

    這樣就很完美的既支持了從數(shù)據(jù)庫的讀取數(shù)據(jù), 又可以自由選擇 Perl5 和 AntPath 兩種風(fēng)格的配置, 最后不要忘了給 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>

     這段時(shí)間看了很多人對(duì) Acegi 的評(píng)價(jià), 有不少觀點(diǎn)認(rèn)為 Acegi 的配置太過繁瑣, 其實(shí)權(quán)限控制本來就不是一件很輕松的事, Acegi 用 AOP 實(shí)現(xiàn), 配置文件的確有些繁瑣, 但是只要一個(gè)配置文件就解決了整個(gè)系統(tǒng)的權(quán)限問題, 可謂一勞永逸, 相比較在 Action 中實(shí)現(xiàn)應(yīng)該還是利遠(yuǎn)大于弊, 也有人說用 WebWork 的 Interceptor 實(shí)現(xiàn), 雖然也是不錯(cuò)的 solution, 但是不要忘了, 并不是所有的項(xiàng)目都使用 webwork , 假如有一個(gè) struts 的項(xiàng)目, 權(quán)限控制就會(huì)有移植性的問題.

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

    posted on 2007-12-25 09:35 蘆葦 閱讀(1876) 評(píng)論(0)  編輯  收藏 所屬分類: Spring
    主站蜘蛛池模板: 亚洲毛片免费观看| 日本阿v免费费视频完整版| 亚洲综合小说另类图片动图 | 亚洲校园春色小说| 国产成人亚洲精品狼色在线| 亚洲人成网站免费播放| 久久免费动漫品精老司机| 性生大片视频免费观看一级 | 麻豆一区二区免费播放网站 | 久久久久无码精品亚洲日韩| 亚洲AV无码一区二三区| 午夜免费福利在线| 免费人成视频在线| 18国产精品白浆在线观看免费 | 国产成人精品曰本亚洲79ren| 国产色爽免费视频| 在线播放免费人成视频在线观看| 色片在线免费观看| 亚洲黄色免费在线观看| 特级精品毛片免费观看| 亚洲免费精彩视频在线观看| 两个人看的www免费高清| 久久av免费天堂小草播放| 一二三四在线观看免费中文在线观看| 亚洲爆乳大丰满无码专区| 亚洲国产无线乱码在线观看| 亚洲永久在线观看| 亚洲AV无码无限在线观看不卡| 亚洲另类精品xxxx人妖| 亚洲sss综合天堂久久久| 狠狠色香婷婷久久亚洲精品| 亚洲国产熟亚洲女视频| 国产午夜亚洲精品| 亚洲欧美日韩中文二区| 亚洲第一街区偷拍街拍| 看一级毛片免费观看视频| 午夜免费国产体验区免费的| 国产精品免费久久久久影院| 99视频在线免费观看| 亚洲欧洲免费视频| 国产精品怡红院永久免费|