Spring web过滤器-委派过滤器代理(DelegatingFilterProxy/FilterChainProxy)——Spring Security3源码分析

http://blog.chinaunix.net/uid-7374279-id-4246259.html

http://blog.csdn.net/geloin/article/details/7441937

整理于上面文章,

Spring security应用时会用到的一个重要组件

org.springframework.web.filter中有一个特殊的类——DelegatingFilterProxy,该类其实并不能说是一个过滤器,它的原型是FilterToBeanProxy即将它作为spring的bean,DelegatingFilterProxy就是一个对于servlet filter的代理,由spring来管理。

该类提供了在web.xml和application context之间的联系,

原理是,DelegatingFilterProxy类是一个代理类,所有的请求都会首先发到这个filter代理,然后再按照”filter-name”委派到spring中的这个bean,


用这个类的好处

1.主要是通过Spring容器来管理servlet filter的生命周期,

2.还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,

如果不配置DelegatingFilterProxy,则由于filter比bean先加载,也就是容器或者Tomcat会先加载filter指定的类到container中,

这样filter中注入的spring bean就为null了,

3.另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。

在Spring Security中就是使用该类进行设置。即在web.xml中配置该过滤器,然后在spring security相关的配置中设置相应的过滤器bean。但是该类是spring-web包下的类,不属于Spring Security类


其他可以通过web.xml传递的参数如下:

(1) contextAttribute,使用委派Bean的范围,其值必须从org.springframework.context.ApplicationContext.WebApplicationContext中取得,默认值是session其他可选的有request、globalSession和application

(2) targetFilterLifecycle,是否调用Filter的init和destroy方法,默认为false。如果要保留Filter原有的init,destroy方法的调用,还需要配置初始化参数targetFilterLifecycle为true,该参数默认为false

(3)targetBeanName,被代理的过滤器的bean的名字,这个beanspring中的名字testBean,该bean的类必须实现Filter接口, 或者可以不用配置这个参数,这样spring容器中所有实现了Filter接口的类都被代理实际就是把Servlet容器中的filters同spring容器中的bean关联起来,方便spring进行管理


所以DelegationgFilterProxy的全项配置信息如下:

        <filter>
		<filter-name>testFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>testBean</param-value>
		</init-param>
                <init-param>
			<param-name>contextAttribute</param-name>
			<param-value>session</param-value>
		</init-param>
                <init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>false</param-value>
		</init-param>
	</filter>        
        <filter-mapping>
		<filter-name>testFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

======================================================================================

FilterChainProxy初始化——Spring Security3源码分析

http://sishuok.com/forum/blogPost/list/4251.html;jsessionid=737917E57BC92EE9E0E206AB2923A175

从这里开始才是研究 DelegatingFilterProxy/FilterChainProxy 的原理的关键

http://dead-knight.iteye.com/blog/1511389

转载于上面连接

下面关于 spring security的标签解析部分的源码部分没怎么看懂,有时间再看看


很久没有更新博客了,最近对Spring Security做了比较深入的研究。 
spring security的教程网上很多: 
http://lengyun3566.iteye.com/category/153689 
http://wenku.baidu.com/view/b0c0dc0b79563c1ec5da7179.html 

以上教程足够应付在实际项目中使用spring security这一安全框架了。如果想深入研究下,网上的资料就很少了,比如: 
http://www.blogjava.net/SpartaYew/archive/2011/05/19/spingsecurity3.html 
http://www.blogjava.net/youxia/archive/2008/12/07/244883.html 
http://www.cnblogs.com/hzhuxin/archive/2011/12/19/2293730.html 

但还是没有从filter配置开始进行一步一步分析。 
带着很多疑问,逐步拨开spring security3的面纱 
spring security在web.xml中的配置为 

Xml代码  

  1. <filter>  
  2.     <filter-name>springSecurityFilterChain</filter-name>  
  3.     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  4. </filter>  
  5. <filter-mapping>  
  6.     <filter-name>springSecurityFilterChain</filter-name>  
  7.     <url-pattern>/*</url-pattern>  
  8. </filter-mapping>  

一看就知道,这是spring的类,这个类位于org.springframework.web-3.0.1.RELEASE.jar这个jar下面,说明这个类本身是和springSecurity无关。DelegatingFilterProxy类继承于抽象类GenericFilterBean,间接地实现了javax.servlet.Filter接口。细节方面就不一一讲述了。看doFilter方法 

Java代码  

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

这里做了两件事: 

一、initDelegate(wac);//初始化FilterChainProxy 

Java代码  

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

getTargetBeanName()返回的是Filter的name:springSecurityFilterChain 

Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); 

这里根据springSecurityFilterChain的bean name直接获取FilterChainProxy的实例。 

这里大家会产生疑问,springSecurityFilterChain这个bean在哪里定义的呢? 

此时似乎忽略了spring security的bean配置文件了 

Xml代码  

  1. <?xml version=“1.0” encoding=“UTF-8”?>  
  2. <beans:beans xmlns=“http://www.springframework.org/schema/security”  
  3.        xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”  
  4.        xmlns:beans=“http://www.springframework.org/schema/beans”  
  5.        xsi:schemaLocation=”  
  6.        http://www.springframework.org/schema/beans  
  7.        http://www.springframework.org/schema/beans/spring-beans.xsd  
  8.        http://www.springframework.org/schema/security  
  9.        http://www.springframework.org/schema/security/   
  10.               spring-security-3.0.xsd”>  
  11.   <http auto-config=“true”>  
  12.     <intercept-url pattern=“/*” access=“ROLE_USER”/>  
  13.   </http>  
  14.   <authentication-manager alias=“authenticationManager”>  
  15.     <authentication-provider>  
  16.       <user-service>  
  17.         <user authorities=“ROLE_USER” name=“guest” password=“guest”/>  
  18.       </user-service>  
  19.     </authentication-provider>  
  20.   </authentication-manager>   
  21. </beans:beans>  

这是最简单的配置了,同时也是解开springSecurityFilterChain这个bean没有定义的疑问了。 

这里主要利用了spring的自定义标签。具体参见: 

[url] http://www.w3school.com.cn/schema/schema_example.asp[/url] 

首先spring security的标签解析部分的源码包为: 

spring-security-config-3.0.2.RELEASE.jar 

这个jar包的META-INF目录下面有spring.handlers,spring.schemas两个文件,其中spring.schemas文件主要是标签的规范、约束;而spring.handlers这个文件时真正解析自定义标签的类,这个文件的内容为: 

Java代码  

  1. http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler  

从这里可以看出来spring security的标签解析由org.springframework.security.config.SecurityNamespaceHandler 

来处理。该类实现接口:NamespaceHandler,spring中自定义标签都要实现该接口,这个接口有三个方法init、parse、decorate,其中init用于自定义标签的初始化,parse用于解析标签,decorate用于装饰。 

SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作 

Java代码  

  1.    public void init() {  
  2.        // Parsers  
  3.        parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());  
  4.        parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());  
  5.        parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());  
  6.        parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());  
  7.        parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());  
  8.        parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());  
  9.        parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());  
  10.        parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());  
  11. //       registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());  
  12.   
  13.        // Only load the web-namespace parsers if the web classes are available  
  14.        if (ClassUtils.isPresent(“org.springframework.security.web.FilterChainProxy”, getClass().getClassLoader())) {  
  15.            parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());  
  16.            parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());  
  17.            parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());  
  18.            filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();  
  19.            //registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());  
  20.        }  
  21.    }  

可以看出,http的标签解析类注册代码为: 

Java代码  

  1. parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());  

authentication-manager的标签解析类注册代码为: 

Java代码  

  1. parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());  

HttpSecurityBeanDefinitionParser的parse方法源码为: 

Java代码  

  1. public BeanDefinition parse(Element element, ParserContext pc) {  
  2.         CompositeComponentDefinition compositeDef =  
  3.             new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));  
  4.         pc.pushContainingComponent(compositeDef);  
  5.         final Object source = pc.extractSource(element);  
  6.   
  7.         final String portMapperName = createPortMapper(element, pc);  
  8.         final UrlMatcher matcher = createUrlMatcher(element);  
  9.   
  10.         HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher, portMapperName);  
  11.   
  12.         httpBldr.parseInterceptUrlsForEmptyFilterChains();  
  13.         httpBldr.createSecurityContextPersistenceFilter();  
  14.         httpBldr.createSessionManagementFilters();  
  15.   
  16.         ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();  
  17.         BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);  
  18.   
  19.         httpBldr.createServletApiFilter();  
  20.         httpBldr.createChannelProcessingFilter();  
  21.         httpBldr.createFilterSecurityInterceptor(authenticationManager);  
  22.   
  23.         AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,  
  24.                 httpBldr.isAllowSessionCreation(), portMapperName);  
  25.   
  26.         authBldr.createAnonymousFilter();  
  27.         authBldr.createRememberMeFilter(authenticationManager);  
  28.         authBldr.createRequestCache();  
  29.         authBldr.createBasicFilter(authenticationManager);  
  30.         authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);  
  31.         authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);  
  32.         authBldr.createX509Filter(authenticationManager);  
  33.         authBldr.createLogoutFilter();  
  34.         authBldr.createLoginPageFilterIfNeeded();  
  35.         authBldr.createUserServiceInjector();  
  36.         authBldr.createExceptionTranslationFilter();  
  37.   
  38.         List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();  
  39.   
  40.         unorderedFilterChain.addAll(httpBldr.getFilters());  
  41.         unorderedFilterChain.addAll(authBldr.getFilters());  
  42.   
  43.         authenticationProviders.addAll(authBldr.getProviders());  
  44.   
  45.         BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);  
  46.         requestCacheAwareFilter.getPropertyValues().addPropertyValue(“requestCache”, authBldr.getRequestCache());  
  47.         unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));  
  48.   
  49.         unorderedFilterChain.addAll(buildCustomFilterList(element, pc));  
  50.   
  51.         Collections.sort(unorderedFilterChain, new OrderComparator());  
  52.         checkFilterChainOrder(unorderedFilterChain, pc, source);  
  53.   
  54.         List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();  
  55.   
  56.         for (OrderDecorator od : unorderedFilterChain) {  
  57.             filterChain.add(od.bean);  
  58.         }  
  59.   
  60.         ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = httpBldr.getFilterChainMap();  
  61.         BeanDefinition universalMatch = new RootBeanDefinition(String.class);  
  62.         universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());  
  63.         filterChainMap.put(universalMatch, filterChain);  
  64.   
  65.         registerFilterChainProxy(pc, filterChainMap, matcher, source);  
  66.   
  67.         pc.popAndRegisterContainingComponent();  
  68.         return null;  
  69.     }  

很多spring security的教程都会说http标签配置了auto-config=”true”属性,spring security就会自动配置好了过滤器链。但是这些过滤器是如何添加到链中的呢,教程没说。 

但是上面的代码已经告诉我们,就在这里设置的 

Java代码  

  1. httpBldr.createSecurityContextPersistenceFilter();  
  2. httpBldr.createSessionManagementFilters();  
  3. httpBldr.createServletApiFilter();  
  4. httpBldr.createChannelProcessingFilter();  
  5. httpBldr.createFilterSecurityInterceptor(authenticationManager);  
  6. …………  
  7. authBldr.createAnonymousFilter();  
  8. authBldr.createRememberMeFilter(authenticationManager);  
  9. authBldr.createRequestCache();  
  10. authBldr.createBasicFilter(authenticationManager);  
  11. authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);  
  12. authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);  
  13. authBldr.createX509Filter(authenticationManager);  
  14. authBldr.createLogoutFilter();  
  15. authBldr.createLoginPageFilterIfNeeded();  
  16. authBldr.createUserServiceInjector();  
  17. authBldr.createExceptionTranslationFilter();  

具体create分析下一篇再细说。接下来完成Filter的排序、并添加到filterChainMap集合中 

Java代码  

  1. List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();  
  2.   
  3.         unorderedFilterChain.addAll(httpBldr.getFilters());  
  4.         unorderedFilterChain.addAll(authBldr.getFilters());  
  5.   
  6.         authenticationProviders.addAll(authBldr.getProviders());  
  7.   
  8.         BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);  
  9.         requestCacheAwareFilter.getPropertyValues().addPropertyValue(“requestCache”, authBldr.getRequestCache());  
  10.         unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));  
  11.   
  12.         unorderedFilterChain.addAll(buildCustomFilterList(element, pc));  
  13.   
  14.         Collections.sort(unorderedFilterChain, new OrderComparator());  
  15.         checkFilterChainOrder(unorderedFilterChain, pc, source);  
  16.   
  17.         List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();  
  18.   
  19.         for (OrderDecorator od : unorderedFilterChain) {  
  20.             filterChain.add(od.bean);  
  21.         }  
  22.   
  23.         ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = httpBldr.getFilterChainMap();  
  24.         BeanDefinition universalMatch = new RootBeanDefinition(String.class);  
  25.         universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());  
  26.         filterChainMap.put(universalMatch, filterChain);  

此时,已经为FilterChainProxy提供了必须的参数,接下来就是该完成FilterChainProxy的bean定义过程了 

Java代码  

  1. registerFilterChainProxy(pc, filterChainMap, matcher, source);  

Java代码  

  1. private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, UrlMatcher matcher, Object source) {  
  2.     if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {  
  3.         pc.getReaderContext().error(“Duplicate <http> element detected”, source);  
  4.     }  
  5.     //定义FilterChainProxy的BeanDefinition构造对象  
  6.      BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);  
  7.     fcpBldr.getRawBeanDefinition().setSource(source);  
  8.     fcpBldr.addPropertyValue(“matcher”, matcher);  
  9.     fcpBldr.addPropertyValue(“stripQueryStringFromUrls”, Boolean.valueOf(matcher instanceof AntUrlPathMatcher));  
  10.     //注入过滤器链  
  11.     fcpBldr.addPropertyValue(“filterChainMap”, filterChainMap);  
  12.     BeanDefinition fcpBean = fcpBldr.getBeanDefinition();  
  13.     //注册bean  
  14.     pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));  
  15.     //注册bean的alias,其中别名为springSecurityFilterChain        
  16.     pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);  
  17. }  

这里需要说明的是BeanDefinitionBuilder类,该类能够动态创建


spring的bean,并通过ParserContext完成bean的注册,而不需要


在xml中进行配置。 


此时FilterChainProxy实例化过程已经完成。 


二、invokeDelegate(delegateToUse, request, response, filterChain); 
//调用代理类的doFilter方法 

Java代码  

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

执行第一步获取的FilterChainProxy实例的doFilter方法。 

通过以上分析,对FilterChainProxy如何产生的,以及Spring Security的标签是如何解析有了大体的认识。 

具体标签的解析、Filter链的执行,下次再更新……

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/liangxw1/article/details/51087657
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞