spring DelegatingFilterProxy的原理及運用


DelegatingFilterProxy的原理及使用

DelegatingFilterProxy就是一個對於servlet filter的代理,用這個類的好處主要是通過Spring容器來管理servlet filter的生命周期,還有就是如果filter中需要一些Spring容器的實例,可以通過spring直接注入,另外讀取一些配置文件這些便利的操作都可以通過Spring來配置實現。

 

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>

 

然后在Spring的配置文件中,配置具體的Filter類的實例。

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

 

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

 

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

 

如果要保留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> 

-------------------------------------------------- 
1、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>  



2、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" />  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM