Spring Security4.0.3源碼分析之http標籤解析

最近在學習安全框架Spring Security,想弄清楚其中實現的具體步驟,於是下定決心,研究一下Spring Security源碼,這篇博客的目的是想把學習過程記錄下來。學習過程中主要參考了http://dead-knight.iteye.com/category/220917大神的博客,然後在其基礎上,進行更詳細的說明

在FilterChainProxy初始化的過程中,大概描述了標籤解析的一些步驟,但是還不夠詳細,該篇博客將進行更細緻的說明。HTTP標籤的解析由HTTP標籤解析類org.springframework.security.config.http.HttpSecurityBeanDefinitionParser完成

1.解析過程

public BeanDefinition parse(Element element, ParserContext pc) {
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
    pc.pushContainingComponent(compositeDef);

    // 創建了listFactoryBean實例和springSecurityFilterChain實例
    registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));

    // 獲取listFactoryBean實例
    BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS);
    List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();

    // 創建過濾器鏈 移步2
    filterChains.add(createFilterChain(element, pc));

    pc.popAndRegisterContainingComponent();
    return null;
}

2.創建過濾器鏈

private BeanReference createFilterChain(Element element, ParserContext pc) {
    // 判斷是否需要Security攔截
    boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED));

    if (!secured) {
        // 如果沒配置pattern屬性並且配置了request-matcher-ref爲空 添加錯誤信息
        if (!StringUtils.hasText(element.getAttribute(ATT_PATH_PATTERN)) && !StringUtils.hasText(ATT_REQUEST_MATCHER_REF)) {
            pc.getReaderContext().error("The '" + ATT_SECURED + "' attribute must be used in combination with" + " the '" + ATT_PATH_PATTERN + "' or '" + ATT_REQUEST_MATCHER_REF + "' attributes.", pc.extractSource(element));
        }

        for (int n = 0; n < element.getChildNodes().getLength(); n++) {
            // 如果有子節點則添加錯誤信息
            if (element.getChildNodes().item(n) instanceof Element) {
                pc.getReaderContext().error("If you are using <http> to define an unsecured pattern, " + "it cannot contain child elements.", pc.extractSource(element));
            }
        }

        // 創建過濾器鏈
        return createSecurityFilterChainBean(element, pc, Collections.emptyList());
    }

    // portMapper、portResolver主要提供給SSL相關類使用
    final BeanReference portMapper = createPortMapper(element, pc);
    final BeanReference portResolver = createPortResolver(portMapper, pc);

    // 新建一個空的authenticationProviders集合 
    ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
    // 通過空的authenticationProviders集合產生一個AuthenticationManager的bean定義
    BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders);

    // 是否全採用默認配置
    boolean forceAutoConfig = isDefaultHttpConfig(element);
    // 詳解移步3
    HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
    // 詳解移步4
    AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());

    // 配置logoutHandlers
    httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
    httpBldr.setEntryPoint(authBldr.getEntryPointBean());
    httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());

    // 向AuthenticationProviders中添加provider  
    authenticationProviders.addAll(authBldr.getProviders());

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

    // 向FilterChain鏈中添加filters  
    unorderedFilterChain.addAll(httpBldr.getFilters());
    unorderedFilterChain.addAll(authBldr.getFilters());

    // 添加自定義的Filter,也就是custom-filter標籤定義的Filter  
    unorderedFilterChain.addAll(buildCustomFilterList(element, pc));

    // 對過濾器進行排序
    Collections.sort(unorderedFilterChain, new OrderComparator());
    // 校驗過濾器是否有效
    checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));

    // The list of filter beans
    List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();

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

    // 創建SecurityFilterChain 詳情移步5
    return createSecurityFilterChainBean(element, pc, filterChain);
}

3.HttpConfigurationBuilder

public HttpConfigurationBuilder(Element element, boolean addAllAuth, ParserContext pc, BeanReference portMapper, BeanReference portResolver, BeanReference authenticationManager) {
    this.httpElt = element;
    this.addAllAuth = addAllAuth;
    this.pc = pc;
    this.portMapper = portMapper;
    this.portResolver = portResolver;
    this.matcherType = MatcherType.fromElement(element);
    // 獲取子標籤intercept-url
    interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);

    for (Element urlElt : interceptUrls) {
        // 判斷子標籤intercept-url是否配置了filters屬性
        // 如果配置了filters屬性添加錯誤消息,因爲Security已經不再支持filters屬性了
        if (StringUtils.hasText(urlElt.getAttribute(ATT_FILTERS))) {
            pc.getReaderContext().error("The use of \"filters='none'\" is no longer supported. Please define a" + " separate <http> element for the pattern you want to exclude and use the attribute" + " \"security='none'\".", pc.extractSource(urlElt));
        }
    }

    // 獲取標籤create-session屬性
    String createSession = element.getAttribute(ATT_CREATE_SESSION);

    if (StringUtils.hasText(createSession)) {
        sessionPolicy = createPolicy(createSession);
    } else {
        // 默認策略
        sessionPolicy = SessionCreationPolicy.IF_REQUIRED;
    }

    // 創建一系列過濾器
    createCsrfFilter();
    createSecurityContextPersistenceFilter();
    createSessionManagementFilters();
    createWebAsyncManagerFilter();
    createRequestCacheFilter();
    createServletApiFilter(authenticationManager);
    createJaasApiFilter();
    createChannelProcessingFilter();
    createFilterSecurityInterceptor(authenticationManager);
    createAddHeadersFilter();
}

4.AuthenticationConfigBuilder

public AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc, SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager, BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
    this.httpElt = element;
    this.pc = pc;
    this.requestCache = requestCache;
    // 是否自動配置
    autoConfig = forceAutoConfig | "true".equals(element.getAttribute(ATT_AUTO_CONFIG));
    // 是否允許session
    this.allowSessionCreation = sessionPolicy != SessionCreationPolicy.NEVER && sessionPolicy != SessionCreationPolicy.STATELESS;
    this.portMapper = portMapper;
    this.portResolver = portResolver;
    this.csrfLogoutHandler = csrfLogoutHandler;

    // 創建一系列過濾器
    createAnonymousFilter();
    createRememberMeFilter(authenticationManager);
    createBasicFilter(authenticationManager);
    createFormLoginFilter(sessionStrategy, authenticationManager);
    createOpenIDLoginFilter(sessionStrategy, authenticationManager);
    createX509Filter(authenticationManager);
    createJeeFilter(authenticationManager);
    createLogoutFilter();
    createLoginPageFilterIfNeeded();
    createUserDetailsServiceFactory();
    createExceptionTranslationFilter();
}

5.創建SecurityFilterChain

private BeanReference createSecurityFilterChainBean(Element element, ParserContext pc, List<?> filterChain) {
    BeanMetadataElement filterChainMatcher;

    String requestMatcherRef = element.getAttribute(ATT_REQUEST_MATCHER_REF);
    String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);

    // 實例化filterChainMatcher
    if (StringUtils.hasText(requestMatcherRef)) {
        if (StringUtils.hasText(filterChainPattern)) {
            // 不能在配置了request-matcher-ref的同時配置pattern
            pc.getReaderContext().error("You can't define a pattern and a request-matcher-ref for the " + "same filter chain", pc.extractSource(element));
        }
        filterChainMatcher = new RuntimeBeanReference(requestMatcherRef);
    }else if (StringUtils.hasText(filterChainPattern)) {
        filterChainMatcher = MatcherType.fromElement(element).createMatcher(filterChainPattern, null);
    }else {
        filterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);
    }

    BeanDefinitionBuilder filterChainBldr = BeanDefinitionBuilder.rootBeanDefinition(DefaultSecurityFilterChain.class);
    filterChainBldr.addConstructorArgValue(filterChainMatcher);
    filterChainBldr.addConstructorArgValue(filterChain);

    BeanDefinition filterChainBean = filterChainBldr.getBeanDefinition();

    // 獲取http標籤的name屬性
    String id = element.getAttribute("name");
    if (!StringUtils.hasText(id)) {
        id = element.getAttribute("id");
        if (!StringUtils.hasText(id)) {
            // 生成ID
            id = pc.getReaderContext().generateBeanName(filterChainBean);
        }
    }

    // 註冊DefaultSecurityFilterChain
    pc.registerBeanComponent(new BeanComponentDefinition(filterChainBean, id));

    return new RuntimeBeanReference(id);
}

至此,大概HTTP標籤的解析已經差不多了,雖然每個Filter的BeanDefinition創建過程還沒有一一細說,但基本步驟如下:

1.通過Filter的類路徑獲取BeanDefinitionBuilder對象,如BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(filterClassName)

2.解析xml標籤屬性,再通過BeanDefinitionBuilder的addPropertyValue,addPropertyReference等方法設置Filter對應BeanDefinition的屬性值,依賴bean 

3.註冊BeanDefinition。通過ParserContext.registerBeanComponent(new BeanComponentDefinition(BeanDefinition,beanId));完成bean的註冊,還可以通過ParserContext.getRegistry().registerAlias方法註冊bean的別名 

實際上,標籤解析就是構造BeanDefinition,然後註冊到beanfactory中,而BeanDefinition就是Spring中定義bean的數據結構
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章