3.1节我们介绍了在一个web应用中,Spring Security的配置实际上也包含了创建过滤器与注册过滤器两个过程。因此本节的源码分析也分为2个部分讲解,第一部分是创建过滤器过程的源码分析,第二部分是注册过程的源码分析。

过滤器创建流程的源码分析

当我们在任意一个类上添加了一个注解@EnableWebSecurity,就可以创建一个名为 springSecurityFilterChain 的Filter。在上一节中,我们是在一个自定义的SecurityConfig类上加了这个注解。SecurityConfig类同时也继承了WebSecurityConfigurerAdapter类,不过需要注意的是,这个过滤器的创建是通过@EnableWebSecurity完成的,与是否继承这个类无关。关于WebSecurityConfigurerAdapter这个类我们在后面会详细讲解,目前所需知道的是,即使我们自定义的类,不继承这个类,也可以自动帮我们创建一个Filter。

我们带着2个问题来考虑这个名字为springSecurityFilterChain 的Filter的创建过程:

1、这个Filter是在哪里创建的?

2、这个Filter的实现类是什么?

1、springSecurityFilterChain 过滤器的创建的位置

为什么在一个类上添加一个@EnableWebSecurity注解后,就可以自动创建一个Filter呢?我们首先看一下@EnableWebSecurity的源码:

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class, ObjectPostProcessorConfiguration.class,
      SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
 
   /**
    * Controls debugging support for Spring Security. Default is false.
    * @return if true, enables debug support with Spring Security
    */
   boolean debug() default false;
}

可以看到,这个注解上面加了@Configuration@EnableGlobalAuthentication@Import三个注解,因此,当我们把这个注解加在某个类之上,就相当于在这个类上面同时加上了这三个注解。在第三章中,我们将这个注解加在SecurityConfig类上,意味着相当于在SecurityConfig类上加上了这个三个注解。

其中@Import这个注解中导入了三个配置类:WebSecurityConfigurationObjectPostProcessorConfigurationSpringWebMvcImportSelector

,我们的Filter实际上就是由WebSecurityConfiguration这个类创建,查看其核心部分源码:

@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
   private WebSecurity webSecurity;
   ....
  private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
....
 
//创建一个名为springSecurityFilterChain的Filter
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
   boolean hasConfigurers = webSecurityConfigurers != null
         && !webSecurityConfigurers.isEmpty();
   if (!hasConfigurers) {
      WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
            .postProcess(new WebSecurityConfigurerAdapter() {
            });
      webSecurity.apply(adapter);
   }
   return webSecurity.build();
 }
....
}

可以看到这个配置类中生成了一个名字为AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME的Filter(读者目前不要过多关注实现细节,后面会详细讲解),这是一个静态常量,其定义如下:

public abstract class AbstractSecurityWebApplicationInitializer implements
        WebApplicationInitializer {
 
    ....
 
    public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
    ...
    }

因此,这段配置的作用实际上是,创建一个Spring Bean,Bean的类型是Filter,名字为springSecurityFilterChain。只要我们保证自定义的SecuirtyConfig类,可以被Spring扫描到,就可以帮助我们创建这个Filter了。

2、springSecurityFilterChain 过滤器的类型是什么

在上面的分析中,我们可以看到Filter的创建时通过WebSecurity对象的build方法完成的。我们先看一下WebSecurity对象的部分文档注释:

img

这段话大致的意思是,WebSecurity由WebSecurityConfiguration创建,而WebSecurity的作用是用于创建一个类型为FilterChainProxy的过滤器,FilterChainProxy是Filter的子类,我们所说的创建一个名字为springSecurityFilterChain的过滤器,实际上过滤器的具体类型就是FilterChainProxy。

到这里实际上我们实际上对于两个答案都已经有了回答。以下部分的源码分析是为喜欢刨根问底的用户专门提供的,用于讲解FilterChainProxy的构建过程,如果读者感觉有难度,可以直接跳过。

在继续分析源码之前,我们需要提示一下,WebSecurity继承了AbstractConfiguredSecurityBuilder,而AbstractConfiguredSecurityBuilder又继承了AbstractSecurityBuilder对象。

我们来具体的看一下WebSecuirty对象build方法的实现,这个方法定义在AbstractSecurityBuilder中,源码如下:

public final O build() throws Exception {
        if (building.compareAndSet(false, true)) {
            object = doBuild();
            return object;
        }
        throw new AlreadyBuiltException("This object has already been built");
    }

在这个方法中又调用了doBuild方法,这个方法是一个抽象方法,在AbstractConfiguredSecurityBuilder类中实现:

@Override
    protected final O doBuild() throws Exception {
        synchronized (configurers) {
            buildState = BuildState.INITIALIZING;
 
            beforeInit();
            init();
 
            buildState = BuildState.CONFIGURING;
 
            beforeConfigure();
            configure();
 
            buildState = BuildState.BUILDING;
 
            O result = performBuild();//构建返回结果
 
            buildState = BuildState.BUILT;
 
            return result;
        }
    }

在这个方法中,我们需要关注的是performBuild方法,因为这个方法最终返回了结果,这又是一个抽象方法,具体的实现定义在WebSecurity对象中。如下:

@Override
    protected Filter performBuild() throws Exception {
        Assert.state(
                !securityFilterChainBuilders.isEmpty(),
                "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. 
                Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. 
                More advanced users can invoke "
                        + WebSecurity.class.getSimpleName()
                        + ".addSecurityFilterChainBuilder directly");
        int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
        List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
                chainSize);
        for (RequestMatcher ignoredRequest : ignoredRequests) {
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
        }
        for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
            securityFilterChains.add(securityFilterChainBuilder.build());
        }
        //创建FilterChainProxy 对象
        FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
        if (httpFirewall != null) {
            filterChainProxy.setFirewall(httpFirewall);
        }
        filterChainProxy.afterPropertiesSet();
 
        Filter result = filterChainProxy;
        if (debugEnabled) {
            logger.warn("\n\n"
                    + "********************************************************************\n"
                    + "**********        Security debugging is enabled.       *************\n"
                    + "**********    This may include sensitive information.  *************\n"
                    + "**********      Do not use in a production system!     *************\n"
                    + "********************************************************************\n\n");
            result = new DebugFilter(filterChainProxy);
        }
        postBuildAction.run();
        return result;
    }

可以看到最终返回的Filter类型,的确是FilterChainProxy对象。 过滤器注册过程的源码分析

笔者以读者在项目中使用了Spring MVC的情况进行源码分析讲解注册流程。

在servlet3.0规范中,提供了一个javax.servlet.ServletContainerInitializer接口,来帮助我们做web应用的初始化操作,例如动态注册Servelt、Filter、Listener等。定义如下:

package javax.servlet;
 
import java.util.Set;
public interface ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx)
        throws ServletException; 
}

假设我们的项目中使用了SpringMVC,那么肯定会jar依赖spring-web-x.x.x.jar。在这个jar中,我们可以找到以下的文件/META-INF/services/javax.servlet.ServletContainerInitializer。以笔者的依赖为例,这个文件中的内容如下:

img

根据servlet3.0规范,servlet容器要负责创建这个文件中定义的类,在这里就是SpringServletContainerInitializer类,其需要是ServletContainerInitializer的实现,因此其必然实现了onStart()方法,源码如下:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {
 
        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
 
        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                //对于非接口、非抽象类的class对象,通过反射创建实例
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer) waiClass.newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }
 
        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }
 
        AnnotationAwareOrderComparator.sort(initializers);
        servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
                //调用所有实例的onStart方法
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
 
}

这个类上有一个注解@HandlesTypes(WebApplicationInitializer.class),根据Servelt3.0规范,容器要负责以Set集合的方式传递注解中指定的类型的所有子类(包括子接口、实现类等)的class对象。因此WebApplicationInitializer对象的所有子类(包括子接口等)都会在调用SpringServletContainerInitializer类的onStart方法的时候被传入。而Spring Security的AbstractSecurityWebApplicationInitializer类是Spring web提供的WebApplicationInitializer的抽象子类,因此会被传入。而WebApplicationInitializer也是一个接口,其也定义了一个onstart方法,在代码的最后,调用了所有WebApplicationInitializer子类实例的onstart方法。不过由于AbstractSecurityWebApplicationInitializer是抽象类,而代码中if判断逻辑指明了,只有普通的java类才会被创建实例,才有机会被调用start方法,所以我们必须要自定义一个类SecurityWebApplicationInitializer继承AbstractSecurityWebApplicationInitializer,这样AbstractSecurityWebApplicationInitializer中的onstart方法才有机会被调用。而我们的自动注册就是在这个类的onstart方法中被调用。AbstractSecurityWebApplicationInitializer类核心代码如下:

public abstract class AbstractSecurityWebApplicationInitializer implements
        WebApplicationInitializer {
        .....
        public final void onStartup(ServletContext servletContext) throws ServletException {
        beforeSpringSecurityFilterChain(servletContext);
        if (configurationClasses != null) {
            AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
            rootAppContext.register(configurationClasses);
            servletContext.addListener(new ContextLoaderListener(rootAppContext));
        }
        if (enableHttpSessionEventPublisher()) {
            servletContext
                    .addListener("org.springframework.security.web.session.HttpSessionEventPublisher");
        }
        servletContext.setSessionTrackingModes(getSessionTrackingModes());
        insertSpringSecurityFilterChain(servletContext);//完成springSecurityFilterChain的注册
        afterSpringSecurityFilterChain(servletContext);
    }
    .....
    }

上述代码中的insertSpringSecurityFilterChain(servletContext)完成了事实上的Filter的注册。(在此不再继续分析源码,只是讲解流程,以免涉及过多了基础知识,读者本应该掌握这些的)。因为我们之前创建的Filter实际上是Spring Context中的一个Bean,因此当我们在insertSpringSecurityFilterChain方法中传入servletContext的时候,实际上我们就可以根据bean的名称"springSecurityFilterChain"获取到这个Bean。Spring Web会根据创建一个代理的Filter类,DelegatingFilterProxy,这个类中维护了一个非常重要的字段targetBeanName,在我们这里这个字段的值是springSecurityFilterChain。而DelegatingFilterProxy在执行的时候,会根据targetBeanName,找到对应的以Spring Bean方式提供的Filter来处理。因此事实上完成了注册流程。

[转载,原文链接](

Last modification:December 21st, 2018 at 11:32 am
If you think my article is useful to you, please feel free to appreciate