`
Dead_knight
  • 浏览: 1202265 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
752c8642-b795-3fe6-946e-a4e845bffdec
Spring Securi...
浏览量:240604
33caa84e-18a6-3036-a82b-6e2106a4de63
clojure专题
浏览量:49056
E17ca077-44df-3816-a3fe-471c43f6e1e5
WebLogic11g
浏览量:237060
社区版块
存档分类
最新评论

Spring Security3源码分析-FilterChainProxy初始化

阅读更多
很久没有更新博客了,最近对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中的配置为
    <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>

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

这里做了两件事:
一、initDelegate(wac);//初始化FilterChainProxy
	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

getTargetBeanName()返回的是Filter的name:springSecurityFilterChain
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
这里根据springSecurityFilterChain的bean name直接获取FilterChainProxy的实例。
这里大家会产生疑问,springSecurityFilterChain这个bean在哪里定义的呢?
此时似乎忽略了spring security的bean配置文件了
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:beans="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/ 
              spring-security-3.0.xsd">
  <http auto-config="true">
    <intercept-url pattern="/*" access="ROLE_USER"/>
  </http>
  <authentication-manager alias="authenticationManager">
    <authentication-provider>
      <user-service>
        <user authorities="ROLE_USER" name="guest" password="guest"/>
      </user-service>
    </authentication-provider>
  </authentication-manager> 
</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这个文件时真正解析自定义标签的类,这个文件的内容为:
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方法完成了标签解析类的注册工作
    public void init() {
        // Parsers
        parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
        parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());
        parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
        parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
        parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
        parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
        parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
        parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
 //       registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());

        // Only load the web-namespace parsers if the web classes are available
        if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) {
            parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
            parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
            parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
            filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
            //registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
        }
    }

可以看出,http的标签解析类注册代码为:
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());

authentication-manager的标签解析类注册代码为:
parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());

HttpSecurityBeanDefinitionParser的parse方法源码为:
public BeanDefinition parse(Element element, ParserContext pc) {
        CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
        pc.pushContainingComponent(compositeDef);
        final Object source = pc.extractSource(element);

        final String portMapperName = createPortMapper(element, pc);
        final UrlMatcher matcher = createUrlMatcher(element);

        HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher, portMapperName);

        httpBldr.parseInterceptUrlsForEmptyFilterChains();
        httpBldr.createSecurityContextPersistenceFilter();
        httpBldr.createSessionManagementFilters();

        ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
        BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);

        httpBldr.createServletApiFilter();
        httpBldr.createChannelProcessingFilter();
        httpBldr.createFilterSecurityInterceptor(authenticationManager);

        AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,
                httpBldr.isAllowSessionCreation(), portMapperName);

        authBldr.createAnonymousFilter();
        authBldr.createRememberMeFilter(authenticationManager);
        authBldr.createRequestCache();
        authBldr.createBasicFilter(authenticationManager);
        authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
        authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
        authBldr.createX509Filter(authenticationManager);
        authBldr.createLogoutFilter();
        authBldr.createLoginPageFilterIfNeeded();
        authBldr.createUserServiceInjector();
        authBldr.createExceptionTranslationFilter();

        List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();

        unorderedFilterChain.addAll(httpBldr.getFilters());
        unorderedFilterChain.addAll(authBldr.getFilters());

        authenticationProviders.addAll(authBldr.getProviders());

        BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
        requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
        unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));

        unorderedFilterChain.addAll(buildCustomFilterList(element, pc));

        Collections.sort(unorderedFilterChain, new OrderComparator());
        checkFilterChainOrder(unorderedFilterChain, pc, source);

        List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();

        for (OrderDecorator od : unorderedFilterChain) {
            filterChain.add(od.bean);
        }

        ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = httpBldr.getFilterChainMap();
        BeanDefinition universalMatch = new RootBeanDefinition(String.class);
        universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());
        filterChainMap.put(universalMatch, filterChain);

        registerFilterChainProxy(pc, filterChainMap, matcher, source);

        pc.popAndRegisterContainingComponent();
        return null;
    }

很多spring security的教程都会说http标签配置了auto-config="true"属性,spring security就会自动配置好了过滤器链。但是这些过滤器是如何添加到链中的呢,教程没说。
但是上面的代码已经告诉我们,就在这里设置的
        httpBldr.createSecurityContextPersistenceFilter();
        httpBldr.createSessionManagementFilters();
        httpBldr.createServletApiFilter();
        httpBldr.createChannelProcessingFilter();
        httpBldr.createFilterSecurityInterceptor(authenticationManager);
        …………
        authBldr.createAnonymousFilter();
        authBldr.createRememberMeFilter(authenticationManager);
        authBldr.createRequestCache();
        authBldr.createBasicFilter(authenticationManager);
        authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
        authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
        authBldr.createX509Filter(authenticationManager);
        authBldr.createLogoutFilter();
        authBldr.createLoginPageFilterIfNeeded();
        authBldr.createUserServiceInjector();
        authBldr.createExceptionTranslationFilter();

具体create分析下一篇再细说。接下来完成Filter的排序、并添加到filterChainMap集合中
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();

        unorderedFilterChain.addAll(httpBldr.getFilters());
        unorderedFilterChain.addAll(authBldr.getFilters());

        authenticationProviders.addAll(authBldr.getProviders());

        BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
        requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
        unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));

        unorderedFilterChain.addAll(buildCustomFilterList(element, pc));

        Collections.sort(unorderedFilterChain, new OrderComparator());
        checkFilterChainOrder(unorderedFilterChain, pc, source);

        List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();

        for (OrderDecorator od : unorderedFilterChain) {
            filterChain.add(od.bean);
        }

        ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = httpBldr.getFilterChainMap();
        BeanDefinition universalMatch = new RootBeanDefinition(String.class);
        universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());
        filterChainMap.put(universalMatch, filterChain);

此时,已经为FilterChainProxy提供了必须的参数,接下来就是该完成FilterChainProxy的bean定义过程了
registerFilterChainProxy(pc, filterChainMap, matcher, source);

    private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, UrlMatcher matcher, Object source) {
        if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
            pc.getReaderContext().error("Duplicate <http> element detected", source);
        }
        //定义FilterChainProxy的BeanDefinition构造对象
         BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
        fcpBldr.getRawBeanDefinition().setSource(source);
        fcpBldr.addPropertyValue("matcher", matcher);
        fcpBldr.addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
        //注入过滤器链
        fcpBldr.addPropertyValue("filterChainMap", filterChainMap);
        BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
        //注册bean
        pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
        //注册bean的alias,其中别名为springSecurityFilterChain      
        pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
    }

这里需要说明的是BeanDefinitionBuilder类,该类能够动态创建spring的bean,并通过ParserContext完成bean的注册,而不需要在xml中进行配置。
此时FilterChainProxy实例化过程已经完成。
二、invokeDelegate(delegateToUse, request, response, filterChain);
//调用代理类的doFilter方法
	protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		delegate.doFilter(request, response, filterChain);
	}

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

通过以上分析,对FilterChainProxy如何产生的,以及Spring Security的标签是如何解析有了大体的认识。
具体标签的解析、Filter链的执行,下次再更新……
分享到:
评论
8 楼 xsd_旧 2015-11-08  
lz 好人
7 楼 Dead_knight 2014-07-17  
alan0509 写道
   if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) { 
           parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser())


有个疑问 请教下 。

  这个 是不是说  只有当在spring容器中 能找到org.springframework.security.web.FilterChainProxy 实例时,才会将http的解析类放进来,
那么就会有个问题,遇到http解析的时候,这个地方的判断应该是false才对。所以不会放进来吧。

这个意思是说只有当前classloader能加载到org.springframework.security.web.FilterChainProxy这个类,才添加解析器,遇到http解析没关系的
6 楼 alan0509 2014-07-16  
   if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) { 
           parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser())


有个疑问 请教下 。

  这个 是不是说  只有当在spring容器中 能找到org.springframework.security.web.FilterChainProxy 实例时,才会将http的解析类放进来,
那么就会有个问题,遇到http解析的时候,这个地方的判断应该是false才对。所以不会放进来吧。
5 楼 勇敢的核桃 2013-12-07  
按照你的思路看了一便源码。。果然了明白了
4 楼 ricoyu 2012-10-04  
你太牛X了, 正好在看SpringSecurity, 在研究它的初始化过程和它内部的具体执行过程, 拜读你的文章让我受益匪浅, 省却了很多摸索的时间, 谢谢你!
3 楼 sqtds 2012-07-26  
Dead_knight 写道
springSecurityFilterChain        
sqtds 写道
文章写的很收益,拜读了,小弟有个问题啊,没看见springSecurityFilterChain在哪里初始化了啊,求教育。。

    private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, UrlMatcher matcher, Object source) {
        if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
            pc.getReaderContext().error("Duplicate <http> element detected", source);
        }
        //定义FilterChainProxy的BeanDefinition构造对象
         BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
        fcpBldr.getRawBeanDefinition().setSource(source);
        fcpBldr.addPropertyValue("matcher", matcher);
        fcpBldr.addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
        //注入过滤器链
        fcpBldr.addPropertyValue("filterChainMap", filterChainMap);
        BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
        //注册bean
        pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
        //注册bean的alias,其中别名为springSecurityFilterChain      
        pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
    }

上面的registerFilterChainProxy方法就是初始化springSecurityFilterChain并注册到spring中的

        pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);

看文章太不仔细了,lz好人啊,还帮忙指出来了
2 楼 Dead_knight 2012-07-26  
springSecurityFilterChain        
sqtds 写道
文章写的很收益,拜读了,小弟有个问题啊,没看见springSecurityFilterChain在哪里初始化了啊,求教育。。

    private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, UrlMatcher matcher, Object source) {
        if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
            pc.getReaderContext().error("Duplicate <http> element detected", source);
        }
        //定义FilterChainProxy的BeanDefinition构造对象
         BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
        fcpBldr.getRawBeanDefinition().setSource(source);
        fcpBldr.addPropertyValue("matcher", matcher);
        fcpBldr.addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
        //注入过滤器链
        fcpBldr.addPropertyValue("filterChainMap", filterChainMap);
        BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
        //注册bean
        pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
        //注册bean的alias,其中别名为springSecurityFilterChain      
        pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
    }

上面的registerFilterChainProxy方法就是初始化springSecurityFilterChain并注册到spring中的

        pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
1 楼 sqtds 2012-07-25  
文章写的很收益,拜读了,小弟有个问题啊,没看见springSecurityFilterChain在哪里初始化了啊,求教育。。

相关推荐

    spring security源码分析.pdf

    ### Spring Security 源码分析知识...以上内容涵盖了 Spring Security 3 的源码分析中几个关键点的具体内容。通过对这些内容的深入学习和理解,可以更好地掌握 Spring Security 的工作原理及其在实际项目中的应用技巧。

    3.springSecurity源码分析1

    在深入分析SpringSecurity源码之前,我们先了解一些基础概念。 1. DelegatingFilterProxy:这是一个Spring框架提供的过滤器,它作为代理来调用实际的Filter。在web.xml中,filter-name设置为...

    Spring_Security3_源码分析

    1. **FilterChainProxy初始化**:FilterChainProxy是Spring Security的核心过滤器链,它负责根据配置定义的过滤器顺序执行安全检查。在启动时,FilterChainProxy会解析安全配置,构建出一个过滤器链列表,用于后续...

    Spring从入门到精通10

    - **定义**:Spring Boot是由Pivotal团队提供的全新框架,其设计目标是用来简化新Spring应用的初始搭建以及开发过程。 - **特点**: - **自动配置**:Spring Boot默认为你选择了一系列的Spring组件版本,并且可以...

    acegi源码解读.txtacegi源码解读.txtacegi源码解读.txt

    `targetBean`值对应于Spring上下文中的bean名称,这意味着所有安全相关的过滤器都将在Spring环境下初始化和管理。 ##### 2. FilterToBeanProxy的运作 `FilterToBeanProxy`负责根据`targetBean`的值,通过Spring...

    挑战Acegi1.0

    监听器配置主要用于启动时加载Spring上下文、初始化日志配置以及处理会话事件等。 #### 三、Spring配置文件:applicationContext-acegi-security.xml 这个文件是Acegi1.0的安全配置核心文件,用于定义过滤器链和...

    Acegi配置指南[整理].pdf

    `init-param` 的 `targetBean` 参数指定了要查找的 Spring Bean 名,即 `filterChainProxy`,这样 Acegi 就可以通过 Spring 的依赖注入来管理这个过滤器,确保了正确的初始化和销毁。 `lazy` 和 `lifecycle` 是两个...

    SSH和SSI等框架常用基础配置web.xml

    这样,Spring容器在启动时会自动加载这些配置文件并初始化。 #### 三、编码配置 ##### 3.1 `CharacterEncodingFilter` ```xml &lt;filter-name&gt;encodingfilter&lt;/filter-name&gt; &lt;filter-class&gt;org.springframework....

    Acegi配置指南

    `targetBean`初始化参数指定了Spring容器中对应的Bean名称,这里是`filterChainProxy`。这确保了Acegi的安全过滤器链能够正确地参与到Web应用的生命周期中。 然后,在Spring的配置文件(如`acegi.xml`)中,需要...

    web LIstener

    例如,可以通过实现`ServletRequestAttributeListener`来监听请求属性的变化,或者实现`FilterChainProxy`来实现Spring Security的过滤逻辑。 在"webListener"的压缩包中,很可能包含了示例代码或者教程,展示了...

Global site tag (gtag.js) - Google Analytics