spring DelegatingFilterProxy的原理及運用

DelegatingFilterProxy的原理及使用java

DelegatingFilterProxy就是一個對於servlet filter的代理,用這個類的好處主要是經過Spring容器來管理servlet filter的生命週期,還有就是若是filter中須要一些Spring容器的實例,能夠經過spring直接注入,另外讀取一些配置文件這些便利的操做均可以經過Spring來配置實現。web

 

DelegatingFilterProxy的使用方法,正則表達式

 

首先在web.xml中配置:spring

<filter>
< filter-name>myFilter</filter-name>
< filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
< filter-name>myFilter</filter-name>
< url-pattern>/*</url-pattern>
</filter-mapping>數據庫

 

而後在Spring的配置文件中,配置具體的Filter類的實例。瀏覽器

<bean name="myFilter" class="com.*.MyFilter"></bean>安全

 

在Spring中配置的bean的name要和web.xml中的<filter-name>同樣cookie

 

或者在DelegatingFilterProxy的filter配置中配置初始參數:targetBeanName,對應到Spring配置中的beannamesession

 

若是要保留Filter原有的init,destroy方法的調用,還須要配置初始化參數targetFilterLifecycle爲true,該參數默認爲false架構

----------------------------------------------------

使用過springSecurity的朋友都知道,首先須要在web.xml進行如下配置,

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 </filter>

<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>

 </filter-mapping>

從這個配置中,可能會給咱們形成一個錯覺,覺得DelegatingFilterProxy類就是springSecurity的入口,但其實這個類位於spring-web-3.0.5.RELEASE.jar這個jar下面,說明這個類自己是和springSecurity無關。DelegatingFilterProxy類繼承於抽象類GenericFilterBean,間接地implement 了javax.servlet.Filter接口,Servlet容器在啓動時,首先會調用Filter的init方法,GenericFilterBean的做用主要是能夠把Filter的初始化參數自動地set到繼承於GenericFilterBean類的Filter中去。在其init方法的以下代碼就是作了這個事:

1
2
3
4
5
6
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);

 另外在init方法中調用了initFilterBean()方法,該方法是GenericFilterBean類是特意留給子類擴展用的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected void initFilterBean() throws ServletException {
        // If no target bean name specified, use filter name.
        if (this.targetBeanName == null) {
            this.targetBeanName = getFilterName();
        }
 
        // Fetch Spring root application context and initialize the delegate early,
        // if possible. If the root application context will be started after this
        // filter proxy, we'll have to resort to lazy initialization.
        synchronized (this.delegateMonitor) {
            WebApplicationContext wac = findWebApplicationContext();
            if (wac != null) {
                this.delegate = initDelegate(wac);
            }
        }
    }

 能夠看出上述代碼首先看Filter是否提供了targetBeanName初始化參數,若是沒有提供則直接使用filter的name作爲beanName,產生了beanName後,因爲咱們在web.xml的filter的name是springSecurityFilterChain,從spring的IOC容器中取出bean的代碼是initDelegate方法,下面是該方法代碼:

1
2
3
4
5
6
7
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
}

 經過跟蹤代碼,發現取出的bean是org.springframework.security.FilterChainProxy,該類也是繼承於GenericFilterBean,取出bean後,判斷targetFilterLifecycle屬性是false仍是true,決定是否調用該類的init方法。這個FilterChainProxy bean實例最終被保存在DelegatingFilterProxy類的delegate屬性裏,

下面看一下DelegatingFilterProxy類的doFilter方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
 
        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = null;
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
                }
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
 
        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }

 真正要關注invokeDelegate(delegateToUse, request, response, filterChain);這句代碼,在下面能夠看出DelegatingFilterProxy類實際是用其delegate屬性即org.springframework.security.FilterChainProxy實例的doFilter方法來響應請求。

1
2
3
4
5
6
protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
 
        delegate.doFilter(request, response, filterChain);
    }

 

以上就是DelegatingFilterProxy類的一些內部運行機制,其實主要做用就是一個代理模式的應用,能夠把servlet 容器中的filter同spring容器中的bean關聯起來。

----------------------------------------------------------------

安全過濾器鏈 
Spring Security的web架構是徹底基於標準的servlet過濾器的。 它沒有在內部使用servlet或任何其餘基於servlet的框架(好比spring mvc), 因此它沒有與任何特定的web技術強行關聯。 它只管處理HttpServletRequest 和HttpServletResponse,不關心請求時來自瀏覽器,web服務客戶端,HttpInvoker仍是一個AJAX應用。 

Spring Security維護了一個過濾器鏈,每一個過濾器擁有特定的功能,過濾器須要服務也會對應添加和刪除。 過濾器的次序是很是重要的,它們之間都有依賴關係。 若是你已經使用了命名空間配置,過濾器會自動幫你配置, 你不須要定義任何Spring Bean,可是有時候你須要徹底控制Spring過濾器鏈, 由於你使用了命名空間沒有提供的特性,或者你須要使用你本身自定義的類。 

1. DelegatingFilterProxy 
當使用servlet過濾器時,你很須要在你的web.xml中聲明它們, 它們可能被servlet容器忽略。在Spring Security,過濾器類也是定義在xml中的spring bean, 所以能夠得到Spring的依賴注入機制和生命週期接口。 spring的DelegatingFilterProxy提供了在 web.xml和application context之間的聯繫。 

當使用DelegatingFilterProxy,你會看到像 web.xml文件中的這樣內容: 

<filter> <filter-name>myFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 
注意這個過濾器實際上是一個DelegatingFilterProxy,這個過濾器裏沒有實現過濾器的任何邏輯。 DelegatingFilterProxy作的事情是代理Filter的方法,從application context裏得到bean。 這讓bean能夠得到spring web application context的生命週期支持,使配置較爲輕便。 bean必須實現javax.servlet.Filter接口,它必須和filter-name裏定義的名稱是同樣的。查看DelegatingFilterProxy的javadoc得到更多信息。 

2. FilterChainProxy 
如今應該清楚了,你能夠聲明每一個Spring Security過濾器bean,你在application context中須要的。 把一個DelegatingFilterProxy入口添加到web.xml, 確認它們的次序是正確的。 這是一種繁瑣的方式,會讓web.xml顯得十分雜亂,若是咱們配置了太多過濾器的話。 咱們最好添加一個單獨的入口,在web.xml中,而後在application context中處理實體, 管理咱們的web安全bean。 這就是FilterChainProxy所作的事情。它使用DelegatingFilterProxy (就像上面例子中那樣),可是對應的class是org.springframework.security.web.FilterChainProxy。 過濾器鏈是在application context中聲明的。這裏有一個例子: 

<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <sec:filter-chain-map path-type="ant"> <sec:filter-chain pattern="/webServices/**" filters=" securityContextPersistenceFilterWithASCFalse, basicAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> <sec:filter-chain pattern="/**" filters=" securityContextPersistenceFilterWithASCFalse, formLoginFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> </sec:filter-chain-map> </bean> 
你可能注意到FilterSecurityInterceptor聲明的不一樣方式。 命名空間元素filter-chain-map被用來設置安全過濾器鏈。 它映射一個特定的URL模式,到過濾器鏈中,從bean名稱來定義的filters元素。 它同時支持正則表達式和ant路徑,而且只使用第一個出現的匹配URI。 在運行階段FilterChainProxy會定位當前web請求匹配的第一個URI模式,由filters屬性指定的過濾器bean列表將開始處理請求。 過濾器會按照定義的順序依次執行,因此你能夠對處理特定URL的過濾器鏈進行徹底的控制。 

你可能注意到了,咱們在過濾器鏈裏聲明瞭兩個SecurityContextPersistenceFilter(ASC是allowSessionCreation的簡寫,是SecurityContextPersistenceFilter的一個屬性)。 由於web服務歷來不會在請求裏帶上jsessionid,爲每一個用戶代理都建立一個HttpSession徹底是一種浪費。 若是你須要構建一個高等級最高可擴展性的系統,咱們推薦你使用上面的配置方法。 對於小一點兒的項目,使用一個HttpSessionContextIntegrationFilter(讓它的allowSessionCreation默認爲true)就足夠了。 

在有關聲明週期的問題上,若是這些方法被FilterChainProxy本身調用,FilterChainProxy會始終根據下一層的Filter代理init(FilterConfig)和destroy()方法。 這時,FilterChainProxy會保證初始化和銷燬操做只會在Filter上調用一次, 而無論它在過濾器鏈中被聲明瞭多少次)。你控制着全部的抉擇,好比這些方法是否被調用 或targetFilterLifecycle初始化參數DelegatingFilterProxy。 默認狀況下,這個參數是false,servlet容器生命週期調用不會傳播到 DelegatingFilterProxy。 

當咱們瞭解如何使用命名控制配置構建web安全。 咱們使用一個DelegatingFilterProxy,它的名字是「springSecurityFilterChain」。 你應該如今能夠看到FilterChainProxy的名字,它是由命名空間建立的。 

2.1. 繞過過濾器鏈 
經過命名空間,你可使用filters = "none",來提供一個過濾器bean列表。 這會朝向請求模式,使用安全過濾器鏈總體。注意任何匹配這個模式的路徑不會有任何受權或校驗的服務 起做用,它們是能夠自由訪問的。 

3. 過濾器順序 
定義在web.xml裏的過濾器的順序是很是重要的。 不論你實際使用的是哪一個過濾器,<filter-mapping>的順序應該像下面這樣: 

ChannelProcessingFilter,由於它可能須要重定向到其餘協議。 

ConcurrentSessionFilter,由於它不使用SecurityContextHolder功能,可是須要更新 SessionRegistry 來從主體中放映正在進行的請求。 

SecurityContextPersistenceFilter,這樣 SecurityContext能夠在web請求的開始階段經過 SecurityContextHolder創建,而後 SecurityContext的任何修改都會在web請求結束的時候(爲下一個web請求作準備)複製到 HttpSession中。 

驗證執行機制 - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter 等等 - 這樣 SecurityContextHolder 能夠被修改,幷包含一個合法的 Authentication 請求標誌。 

SecurityContextHolderAwareRequestFilter,若是,你使用它,把一個Spring Security提醒HttpServletRequestWrapper安裝到你的servlet容器裏。 

RememberMeAuthenticationFilter,這樣若是以前的驗證執行機制沒有更新 SecurityContextHolder,這個請求提供了一個可使用的remember-me服務的cookie,一個對應的已保存的 Authentication對象會被建立出來。 

AnonymousAuthenticationFilter,這樣若是以前的驗證執行機制沒有更新 SecurityContextHolder,會建立一個匿名 Authentication對象。 

ExceptionTranslationFilter,用來捕捉 Spring Security異常,這樣,可能返回一個HTTP錯誤響應,或者執行一個對應的 AuthenticationEntryPoint。 

FilterSecurityInterceptor,保護web URI。 

4. 使用其餘過濾器 —— 基於框架 
若是你在使用SiteMesh,確認Spring Security過濾器在SiteMesh過濾器以前調用。 這能夠保證SecurityContextHolder爲每一個SiteMesh渲染器及時建立。 



5. 其餘配置例子 
方法一: 
web.xml配置一個 
    <filter> 
        <filter-name>DelegatingFilterProxy</filter-name> 
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
        <init-param> 
            <param-name>targetBeanName</param-name> 
            <param-value>myFilter</param-value>         //本身過濾器的名字 
        </init-param> 
        <init-param> 
            <param-name>targetFilterLifecycle</param-name> 
            <param-value>true</param-value> 
        </init-param> 
    </filter> 

    <filter-mapping> 
        <filter-name>DelegatingFilterProxy</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping> 
方法二: 
web.xml配置一個 
    <filter> 
        <filter-name>myFilter</filter-name> 
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
        <init-param> 
            <param-name>targetFilterLifecycle</param-name> 
            <param-value>true</param-value> 
        </init-param> 
    </filter> 

    <filter-mapping> 
        <filter-name>myFilter</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping> 

方法一或者二不一樣的地方就是在web.xml中的寫法不一樣而已沒有太大的區別,配完web.xml以後還要配置applicationContext.xml中的bean。 
applicationContext.xml配置: 
<bean id="myFilter" class="com.bjtu.filter"> //指名具體的filter類 
    <property name="service">                    //須要注入的具體參數 
        <ref bean="service"/> 
    </property> 
</bean> 
<bean id="service" parent="baseTransactionProxy">//這裏的service封裝了全部對數據庫的操做 
        <property name="target"> 
            <bean class="com.maimaiche.service.MaiMaiCheServiceImpl"> 
             ...... 
             </bean> 
       </property> 
</bean> 

-------------------------------------------------- 
一、web.xml 

Java代碼   收藏代碼
  1. <!-- 全部filter,委託給spring -->  
  2.     <filter>  
  3.         <filter-name>appFilters</filter-name>  
  4.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  5.         <init-param>  
  6.             <param-name>targetFilterLifecycle</param-name>  
  7.             <param-value>true</param-value>  
  8.         </init-param>  
  9.     </filter>  
  10.     <filter-mapping>  
  11.         <filter-name>appFilters</filter-name>  
  12.         <url-pattern>/*</url-pattern>  
  13.     </filter-mapping>  



二、applicationContext-filter.xml 

Java代碼   收藏代碼
    1. <bean id="appFilters" class="org.springframework.security.util.FilterChainProxy">  
    2.         <security:filter-chain-map path-type="ant">  
    3.             <security:filter-chain filters="characterEncodingFilter,commonParamsFilter"  
    4.                 pattern="/**" />  
    5.         </security:filter-chain-map>  
    6.     </bean>  
    7.   
    8.     <bean id="characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter">  
    9.         <property name="encoding" value="UTF-8" />  
    10.         <property name="forceEncoding" value="true" />  
    11.     </bean>  
    12.     <bean id="commonParamsFilter" class="com.renren.wap.fuxi.filter.CommonParamsFilter" />  
相關文章
相關標籤/搜索