Spring Security4.0.3源码分析之FilterChainProxy初始化

最近在学习安全框架Spring Security,想弄清楚其中实现的具体步骤,于是下定决心,研究一下Spring Security源码,这篇博客的目的是想把学习过程记录下来。学习过程中主要参考了http://dead-knight.iteye.com/blog/1511389大神的博客,然后在其基础上,进行更详细的说明

1.Spring Security入口在是在web.xml配置了如下的过滤器,那么这个过滤器到底是怎么工作的呢

<!-- SpringSecurity需要的filter -->
<filter>
    <filter-name>spring-security</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>springSecurityFilterChain</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>spring-security</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

我们打开DelegatingFilterProxy类,发现这个类位于org.springframework.web-3.0.1.RELEASE.jar下面,说明这个类本身是和springSecurity无关。

2.研究一下DelegatingFilterProxy类到底是干嘛的

2.1 DelegatingFilterProxy首先是调用了initFilterBean方法,初始化过滤器

@Override
protected void initFilterBean() throws ServletException {
    synchronized (this.delegateMonitor) {
        if (this.delegate == null) {
            // 如果没配置targetBeanName,那么默认使用过滤器的名字
            if (this.targetBeanName == null) {
                this.targetBeanName = getFilterName();
            }
            // 获取Spring上下文,并且初始化Filter
            // 如果可能的话,在Spring上下文初始化前初始化Filter,我们会使用延迟加载策略
            WebApplicationContext wac = findWebApplicationContext();
            if (wac != null) {
                // 初始化springSecurityFilterChain,详解请移步2.3
                this.delegate = initDelegate(wac);
            }
        }
    }
}

2.2 然后调用了doFilter方法,执行过滤器方法

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
                }
                // 初始化springSecurityFilterChain,详解请移步2.3
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
    }

    // 让委托执行实际的doFilter操作
    // 这里的delegateToUse实际上就是springSecurityFilterChain实例,详解请移步4
    invokeDelegate(delegateToUse, request, response, filterChain);
}

2.3 接着来看看上面提到的initDelegate(wac)方法

// 初始化FilterChainProxy
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
    // getTargetBeanName()返回的是Filter的name:springSecurityFilterChain
    // 根据springSecurityFilterChain的bean name直接获取FilterChainProxy的实例
    Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
    if (isTargetFilterLifecycle()) {
        delegate.init(getFilterConfig());
    }
    return delegate;
}

这里大家会产生疑问,springSecurityFilterChain这个bean在哪里定义的呢?先把这个问题抛开,稍后会作出解答

3. 标签配置的初始化
在spring-security-config-4.0.3.RELEASE.jar包的META-INF目录下有2个文件spring.handlers和spring.schemas。其中spring.schemas文件主要是标签的规范,约束;而spring.handlers这个文件时真正解析自定义标签的类,这个文件的内容为:

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

3.1 标签解析
从上面可以看出来Spring Security的标签解析由org.springframework.security.config.SecurityNamespaceHandler来处理。该类实现接口:NamespaceHandler,Spring中自定义标签都要实现该接口,这个接口有三个方法init、parse、decorate,其中init用于自定义标签的初始化,parse用于解析标签,decorate用于装饰。SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作。打开SecurityNamespaceHandler源码。

public void init() {
    loadParsers();
}

private void loadParsers() {
    // 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());
    parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());

    // Only load the web-namespace parsers if the web classes are available
    if (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {
        parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
        parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
        parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
        parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
        parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
        filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
    }

    if (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) {
        parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER, new WebSocketMessageBrokerSecurityBeanDefinitionParser());
    }
}

上面可以看出SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作,并且http的标签解析类注册代码为:

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

3.2 HttpSecurityBeanDefinitionParser是如何解析HTTP标签的?

@SuppressWarnings({ "unchecked" })
public BeanDefinition parse(Element element, ParserContext pc) {
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
    pc.pushContainingComponent(compositeDef);
    // 这里创建了listFactoryBean实例和springSecurityFilterChain实例,详解移步3.2.1
    registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
    // 获取listFactoryBean实例
    BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS);
    List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
    // 创建过滤器链代码,详解移步3.2.2
    filterChains.add(createFilterChain(element, pc));
    pc.popAndRegisterContainingComponent();
    return null;
}

3.2.1 registerFilterChainProxyIfNecessary分析

static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
    // 判断是否已经注册了FILTER_CHAIN_PROXY,若已经注册则直接返回
    if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
        return;
    }
    // 注册ListFactoryBean
    BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
    listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
    pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS));

    // 注册FilterChainProxy
    BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
    fcpBldr.getRawBeanDefinition().setSource(source);
    fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);
    fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
    BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
    pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
    // 此处为FILTER_CHAIN_PROXY取别名为springSecurityFilterChain
    pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}

抛开前面的代码不看,由最后一行可以看出这里创建了springSecurityFilterChain实例,到这里终于和前面的挂钩起来了

3.2.2 createFilterChain分析

// 创建过滤器链的代码
private BeanReference createFilterChain(Element element, ParserContext pc) {
    // 判断是否需要SpringSecurity拦截
    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) {
                // 如果element有子节点并且instanceofElement 添加错误信息
                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());
    }

    final BeanReference portMapper = createPortMapper(element, pc);
    final BeanReference portResolver = createPortResolver(portMapper, pc);

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

    boolean forceAutoConfig = isDefaultHttpConfig(element);
    // HttpConfigurationBuilder创建过滤器链,详情移步3.2.2.1
    HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
    // AuthenticationConfigBuilder创建过滤器链,详情移步3.2.2.2
    AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());

    httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
    httpBldr.setEntryPoint(authBldr.getEntryPointBean());
    httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());

    authenticationProviders.addAll(authBldr.getProviders());

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

    // 添加过滤器到unorderedFilterChain
    unorderedFilterChain.addAll(httpBldr.getFilters());
    unorderedFilterChain.addAll(authBldr.getFilters());
    unorderedFilterChain.addAll(buildCustomFilterList(element, pc));

    // 对过滤器链进行排序,详细分析请移步3.2.2.3
    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);
    }
    // 创建过滤器链,详情移步3.2.2.4
    return createSecurityFilterChainBean(element, pc, filterChain);
}

3.2.2.1 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);
    interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);

    for (Element urlElt : interceptUrls) {
        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));
        }
    }

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

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

这里可以看到autoConfig属性,如果该属性为true,SpringSecurity就会自动配置好过滤器链。具体如何设置查看AuthenticationConfigBuilder的create*Filter方法即可看到,具体Create分析下一篇再细说。

3.2.2.3 对过滤器链排序分析

排序主要是由Collections.sort(unorderedFilterChain, new OrderComparator());这行代码实现的

unorderedFilterChain是OrderDecorator的List集合,打开OrderDecorator源码,查看构造函数
public OrderDecorator(BeanMetadataElement bean, SecurityFilters filterOrder) {
    this.bean = bean;
    this.order = filterOrder.getOrder();
}
这里我们发现它其实是根据SecurityFilters定义好的顺序进行排序的

enum SecurityFilters {
    FIRST(Integer.MIN_VALUE), CHANNEL_FILTER, SECURITY_CONTEXT_FILTER, CONCURRENT_SESSION_FILTER,

    /** {@link WebAsyncManagerIntegrationFilter} */
    WEB_ASYNC_MANAGER_FILTER, HEADERS_FILTER, CSRF_FILTER, LOGOUT_FILTER, X509_FILTER, PRE_AUTH_FILTER, CAS_FILTER, FORM_LOGIN_FILTER, OPENID_FILTER, LOGIN_PAGE_FILTER, DIGEST_AUTH_FILTER, BASIC_AUTH_FILTER, REQUEST_CACHE_FILTER, SERVLET_API_SUPPORT_FILTER, JAAS_API_SUPPORT_FILTER, REMEMBER_ME_FILTER, ANONYMOUS_FILTER, SESSION_MANAGEMENT_FILTER, EXCEPTION_TRANSLATION_FILTER, FILTER_SECURITY_INTERCEPTOR, SWITCH_USER_FILTER, LAST(Integer.MAX_VALUE);

    private static final int INTERVAL = 100;
    private final int order;

    private SecurityFilters() {
        order = ordinal() * INTERVAL;
    }

    private SecurityFilters(int order) {
        this.order = order;
    }

    public int getOrder() {
        return order;
    }
}

3.2.2.4 创建过滤器链分析

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);

    if (StringUtils.hasText(requestMatcherRef)) {
        if (StringUtils.hasText(filterChainPattern)) {
            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();

    String id = element.getAttribute("name");
    if (!StringUtils.hasText(id)) {
        id = element.getAttribute("id");
        if (!StringUtils.hasText(id)) {
            id = pc.getReaderContext().generateBeanName(filterChainBean);
        }
    }

    pc.registerBeanComponent(new BeanComponentDefinition(filterChainBean, id));

    return new RuntimeBeanReference(id);
}

4. 调用代理类的doFilter方法

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

通过以上分析,对FilterChainProxy如何产生的,以及Spring Security的标签是如何解析有了大体的认识。具体标签的解析,Filter链的执行,下次再更新……

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