前面两篇文章完成了Spring Securyti初始化过程的绝大部分逻辑的分析,今天我们来看遗留部分:
FilterChainProxy是如何装配到DeletatingFilterProxy中的?

SecurityFilterAutoConfiguration

先来看看SecurityFilterAutoConfiguration,这个配置类在spring.factories中能看到调用入口。

我们看他的securityFilterChainRegistration方法:

@Bean
    @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
            SecurityProperties securityProperties) {
        DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                DEFAULT_FILTER_NAME);
        registration.setOrder(securityProperties.getFilter().getOrder());
        registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
        return registration;
    }

这个方法会把SecurityPropertys也初始化进来,SecurityPropertys我们暂时不做详细分析,后续在分析Spring Security的具体用法的时候可能还会继续碰得到。

然后,new了一个DelegatingFilterProxyRegistrationBean对象,传进去的参数为DEFAULT_FILTER_NAME(=springSecurityFilterChain)。该对象的实例化方法为:

    public DelegatingFilterProxyRegistrationBean(String targetBeanName,
            ServletRegistrationBean<?>... servletRegistrationBeans) {
        super(servletRegistrationBeans);
        Assert.hasLength(targetBeanName, "TargetBeanName must not be null or empty");
        this.targetBeanName = targetBeanName;
        setName(targetBeanName);
    }

我们可以看到,传进去的参数“springSecurityFilterChain”被赋值给了该对象的targetBeanName属性。

ServletContextInitializer

我们先来熟悉一下ServletContextInitializer的类结构:
image.png

Interface used to configure a Servlet 3.0+ context programmatically. Unlike WebApplicationInitializer, classes that implement this interface (and do not implement WebApplicationInitializer) will not be detected by SpringServletContainerInitializer and hence will not be automatically bootstrapped by the Servlet container.
This interface is designed to act in a similar way to ServletContainerInitializer, but have a lifecycle that's managed by Spring and not the Servlet container.

配置Servlet3.0+规范下的servlet上下文,和WebApplicationInitializer不同,实现了本接口的类(没有实现WebApplicationInitializer接口)将不会被SpringServletContainerInitializer自动监测到所以也就不会被Servlet容器自动初始化。本接口被设计为与ServletContainerInitializer的行为类似,但是由Spring来管理其生命周期、而不是有Servlet容器来管理。

大概的意思就是说,SpringMVC框架下,这个接口的实现类将会被初始化为Servlet的上下文,实现Servlet相关功能。

好了,这里先了解个大概,后续分析SpringMVC或者SpringBoot+Tomcat的过程中会弄明白的。

我们现在要知道,这个接口的实现类在Servlet容器启动的过程中会被初始化,他的onStartup接口将会被调用到。

@FunctionalInterface
public interface ServletContextInitializer {

    /**
     * Configure the given {@link ServletContext} with any servlets, filters, listeners
     * context-params and attributes necessary for initialization.
     * @param servletContext the {@code ServletContext} to initialize
     * @throws ServletException if any call against the given {@code ServletContext}
     * throws a {@code ServletException}
     */
    void onStartup(ServletContext servletContext) throws ServletException;

}

ServletWebServerApplicationContext#onRefresh()

SpringBoot内置Tomcat启动的过程中最终会调用到ServletWebServerApplicationContext的onRefresh方法。该方法会调用到createWebServer()方法。之后调用到getSelfInitializer()方法,然后调用selfInitialize方法。
我们可以看到,就是在这个selfInitialize方法中会调用ServletContextInitializer的onStartup方法。

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }

beans中包含了一众servlet初始化对象,其中就有DelegatingFilterProxyRegistrationBean,我们简单跟踪一下。

看一下getServletContextInitializerBeans方法:

    protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
        return new ServletContextInitializerBeans(getBeanFactory());
    }

可以看到new 了一个ServletContextInitializerBeans对象并返回,继续跟踪。

ServletContextInitializerBeans

实例化方法设置属性initializerTypes为ServletContextInitializer.class,之后调用了addServletContextInitializerBeans方法。

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
            Class<? extends ServletContextInitializer>... initializerTypes) {
        this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
                : Collections.singletonList(ServletContextInitializer.class);
        addServletContextInitializerBeans(beanFactory);
        addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
                .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
                .collect(Collectors.toList());
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
        logMappings(this.initializers);
    }

addServletContextInitializerBeans方法:

    private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
        for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
            for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
                    initializerType)) {
                addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
            }
        }
    }

该方法从beanFactory中获取initializerType(ServletContextInitializer.class)的bean,从文章开始分析SecurityFilterAutoConfiguration的过程中我们知道DelegatingFilterProxyRegistrationBean类就属于ServletContextInitializer,所以beans中会包含DelegatingFilterProxyRegistrationBean。

后续的我们就暂时不跟踪了,我们知道获取到了DelegatingFilterProxyRegistrationBean对象,我们返回来继续跟踪该对象的onStartup方法。

RegistrationBean#onStartup

onStartup方法在父类RegistrationBean中实现:

@Override
    public final void onStartup(ServletContext servletContext) throws ServletException {
        String description = getDescription();
        if (!isEnabled()) {
            logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
            return;
        }
        register(description, servletContext);
    }

先来看getDescription()方法,该方法定义在AbstractFilterRegistrationBean中。

AbstractFilterRegistrationBean#getDescription

@Override
    protected String getDescription() {
        Filter filter = getFilter();
        Assert.notNull(filter, "Filter must not be null");
        return "filter " + getOrDeduceName(filter);
    }

继续跟踪getFilter()方法,该方法定义在DelegatingFilterProxyRegistrationBean类中。

DelegatingFilterProxyRegistrationBean#getFilter

该方法一上来就new了一个DelegatingFilterProxy对象并返回,实例化参数传递了targetBeanName,从第一部分对配置类SecurityFilterAutoConfiguration的分析我们知道targetBeanName=“springSecurityFilterChain”,实例化过程中赋值给了DelegatingFilterProxy对象的targetBeaname属性。

@Override
    public DelegatingFilterProxy getFilter() {
        return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {

            @Override
            protected void initFilterBean() throws ServletException {
                // Don't initialize filter bean on init()
            }

        };
    }

创建DelegatingFilterProxy对象之后,后续代码将DelegatingFilterProxy对象加入到servlet容器的filterchain中,在请求提交上来之后,通过DelegatingFilterProxy来实现Spring Security的安全功能。

具体DelegatingFilterProxy怎么加入到servlet容器的过滤器链中的代码就不详细分析了。

接下来我们再简单分析一下DelegatingFilterProxy是怎么将请求委托给Spring Security的安全过滤器的。这个问题的答案应该就在DelegatingFilterProxy的dofilter方法中。

DelegatingFilterProxy#doFilter

代码不长,前面我们已经分析过DelegatingFilterProxy的初始化代码了,我们知道该对象的delegate属性为null,所以代码会走到initDelegate方法中。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized (this.delegateMonitor) {
                delegateToUse = this.delegate;
                if (delegateToUse == null) {
                    WebApplicationContext wac = findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    delegateToUse = initDelegate(wac);
                }
                this.delegate = delegateToUse;
            }
        }

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

initDelegate方法

方法从wac(Spring Ioc容器)中获取名字为targetBeanName、类型为Filter.class的bean。大家还有印象吗?前面我们分析过DelegatingFilterProxy的targetBeanName,就是“springSecurityFilterChain”。

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        String targetBeanName = getTargetBeanName();
        Assert.state(targetBeanName != null, "No target bean name set");
        Filter delegate = wac.getBean(targetBeanName, Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

而名字为“springSecurityFilterChain”的bean是何方神圣呢?如果大家忘记的话,返回去看一下我们前面的文章( Spring Security的初始化过程(2)),就是FilterChainProxy。

至此,下图的所有主要逻辑,也就是Spring Security的过滤器初始化过程,我们已经从代码层分析完毕!!!

image.png

收工!!!

上一篇 Spring Security的初始化过程(2)
下一篇 SpringSecuriryt安全过滤器工作原理


45 声望17 粉丝