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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

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. // Lazily initialize the delegate if necessary.
  4. Filter delegateToUse = null;
  5. synchronized (this.delegateMonitor) {
  6. if (this.delegate == null) {
  7. WebApplicationContext wac = findWebApplicationContext();
  8. if (wac == null) {
  9. throw new IllegalStateException(“No WebApplicationContext found: no ContextLoaderListener registered?”);
  10. }
  11. this.delegate = initDelegate(wac);
  12. }
  13. delegateToUse = this.delegate;
  14. }
  15. // Let the delegate perform the actual doFilter operation.
  16. invokeDelegate(delegateToUse, request, response, filterChain);
  17. }

这里做了两件事:

一、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. // Only load the web-namespace parsers if the web classes are available
  13. if (ClassUtils.isPresent(“org.springframework.security.web.FilterChainProxy”, getClass().getClassLoader())) {
  14. parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
  15. parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
  16. parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
  17. filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
  18. //registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
  19. }
  20. }

可以看出,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. final String portMapperName = createPortMapper(element, pc);
  7. final UrlMatcher matcher = createUrlMatcher(element);
  8. HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher, portMapperName);
  9. httpBldr.parseInterceptUrlsForEmptyFilterChains();
  10. httpBldr.createSecurityContextPersistenceFilter();
  11. httpBldr.createSessionManagementFilters();
  12. ManagedList authenticationProviders = new ManagedList();
  13. BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);
  14. httpBldr.createServletApiFilter();
  15. httpBldr.createChannelProcessingFilter();
  16. httpBldr.createFilterSecurityInterceptor(authenticationManager);
  17. AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,
  18. httpBldr.isAllowSessionCreation(), portMapperName);
  19. authBldr.createAnonymousFilter();
  20. authBldr.createRememberMeFilter(authenticationManager);
  21. authBldr.createRequestCache();
  22. authBldr.createBasicFilter(authenticationManager);
  23. authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
  24. authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
  25. authBldr.createX509Filter(authenticationManager);
  26. authBldr.createLogoutFilter();
  27. authBldr.createLoginPageFilterIfNeeded();
  28. authBldr.createUserServiceInjector();
  29. authBldr.createExceptionTranslationFilter();
  30. List unorderedFilterChain = new ArrayList();
  31. unorderedFilterChain.addAll(httpBldr.getFilters());
  32. unorderedFilterChain.addAll(authBldr.getFilters());
  33. authenticationProviders.addAll(authBldr.getProviders());
  34. BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
  35. requestCacheAwareFilter.getPropertyValues().addPropertyValue(“requestCache”, authBldr.getRequestCache());
  36. unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
  37. unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
  38. Collections.sort(unorderedFilterChain, new OrderComparator());
  39. checkFilterChainOrder(unorderedFilterChain, pc, source);
  40. List filterChain = new ManagedList();
  41. for (OrderDecorator od : unorderedFilterChain) {
  42. filterChain.add(od.bean);
  43. }
  44. ManagedMap<BeanDefinition, List> filterChainMap = httpBldr.getFilterChainMap();
  45. BeanDefinition universalMatch = new RootBeanDefinition(String.class);
  46. universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());
  47. filterChainMap.put(universalMatch, filterChain);
  48. registerFilterChainProxy(pc, filterChainMap, matcher, source);
  49. pc.popAndRegisterContainingComponent();
  50. return null;
  51. }

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

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

Java代码

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

Java代码

  1. private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List> filterChainMap, UrlMatcher matcher, Object source) {
  2. if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
  3. pc.getReaderContext().error(“Duplicate 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链的执行,下次再更新……


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring web过滤器-委派过滤器代理(DelegatingFilterProxy/FilterChainProxy)——Spring Security3源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏