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

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

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

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

最近在学习安全框架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链的执行,下次再更新……


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Security4.0.3源码分析之FilterChainProxy初始化

评论 抢沙发

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

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

支付宝扫一扫打赏

微信扫一扫打赏