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>html

一看就知道,這是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 {java

  // 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;
  }web

  // Let the delegate perform the actual doFilter operation.
  invokeDelegate(delegateToUse, request, response, filterChain);
 }spring

這裏作了兩件事:
1、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>app

這是最簡單的配置了,同時也是解開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());ide

        // 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());
        }
    }ui

能夠看出,http的標籤解析類註冊代碼爲:
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());this

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實例化過程已經完成。
2、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鏈的執行.

相關文章
相關標籤/搜索