在上一篇中我们梳理了spring security 是怎样一步步调用到过滤器链的,下面我们来看下框架是怎么构造过滤器链的,以及过滤器链中主要的过滤器

1.过滤器链的构造

1.1自动配置

        springboot 从2.7开始需要被自动加载的类要写在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里,每一行为配置类的全类名,为了向下兼容目前springboot还支持原来的spring.factories配置,但是打开spring.factories文件看里面的配置很少了已经大部分被移到了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

        在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中我们可以看到如下和spring security相关自动配置:

image.png

其中有一条配置为org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,我们看下SecurityAutoConfiguration的源码如下:

在类中通过使用@EnableConfigurationProperties注解读取配置,并使用@Import导入其他配置

@AutoConfiguration
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
    public SecurityAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean({AuthenticationEventPublisher.class})
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }
}

进入导入的SpringBootWebSecurityConfiguration中源码(因为源码太长,有减缩)如下:

可见在SpringBootWebSecurityConfiguration类中使用了@EnableWebSecurity注解,该注解为springsecurity的启用注解,这就是为啥我们只需要引入pom依赖,不需要任何配置就会自动启用springsecurity的原因。

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
class SpringBootWebSecurityConfiguration {
    SpringBootWebSecurityConfiguration() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnMissingBean(
        name = {"springSecurityFilterChain"}
    )
    @ConditionalOnClass({EnableWebSecurity.class})
    @EnableWebSecurity
    static class WebSecurityEnablerConfiguration {
        WebSecurityEnablerConfiguration() {
        }
    }
    ....

进入EnableWebSecurity 源码如下,可见在类中同样使用了@Import注解来导入其他配置

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
    boolean debug() default false;
}

进入 源码 我们可以看到注入springSecurityFilterChain

@Configuration(
    proxyBeanMethods = false
)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    private WebSecurity webSecurity;
    private Boolean debugEnabled;
    private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
    private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
    private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
   ......
 @Bean(
    name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
    boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
    boolean hasFilterChain = !this.securityFilterChains.isEmpty();
    Assert.state(!hasConfigurers || !hasFilterChain, "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
    if (!hasConfigurers && !hasFilterChain) {
        WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
        });
        this.webSecurity.apply(adapter);
    }

    Iterator var7 = this.securityFilterChains.iterator();

    while(true) {
        while(var7.hasNext()) {
            SecurityFilterChain securityFilterChain = (SecurityFilterChain)var7.next();
            this.webSecurity.addSecurityFilterChainBuilder(() -> {
                return securityFilterChain;
            });
            Iterator var5 = securityFilterChain.getFilters().iterator();

            while(var5.hasNext()) {
                Filter filter = (Filter)var5.next();
                if (filter instanceof FilterSecurityInterceptor) {
                    this.webSecurity.securityInterceptor((FilterSecurityInterceptor)filter);
                    break;
                }
            }
        }

        var7 = this.webSecurityCustomizers.iterator();

        while(var7.hasNext()) {
            WebSecurityCustomizer customizer = (WebSecurityCustomizer)var7.next();
            customizer.customize(this.webSecurity);
        }

        return (Filter)this.webSecurity.build();
    }
}
   ......

下面我们看下springSecurityFilterChain怎么构建的,进入最后一行(Filter)this.webSecurity.build()方法,源码如下:

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
    private AtomicBoolean building = new AtomicBoolean();
    private O object;

    public AbstractSecurityBuilder() {
    }

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

    public final O getObject() {
        if (!this.building.get()) {
            throw new IllegalStateException("This object has not been built");
        } else {
            return this.object;
        }
    }

    protected abstract O doBuild() throws Exception;
}

我们看出在build方法中调用了doBuild()抽象方法,我们再看下这个方法的 实现

protected final O doBuild() throws Exception {
    synchronized(this.configurers) {
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
        this.beforeInit();
        this.init();
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
        this.beforeConfigure();
        this.configure();
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
        O result = this.performBuild();
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT;
        return result;
    }
}

在doBuild的实现方法中,我们看到了调用this.performBuild()方法下面我们进入this.performBuild()源码

protected Filter performBuild() throws Exception {
    Assert.state(!this.securityFilterChainBuilders.isEmpty(), () -> {
        return "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this is done by exposing a SecurityFilterChain bean. More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly";
    });
    int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
    List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize);
    List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList();
    Iterator var4 = this.ignoredRequests.iterator();

    while(var4.hasNext()) {
        RequestMatcher ignoredRequest = (RequestMatcher)var4.next();
        this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest + ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
        SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]);
        securityFilterChains.add(securityFilterChain);
        requestMatcherPrivilegeEvaluatorsEntries.add(this.getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
    }

    var4 = this.securityFilterChainBuilders.iterator();

    while(var4.hasNext()) {
        SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var4.next();
        //为每个httpSecurity生成一条过滤器链
        SecurityFilterChain securityFilterChain = (SecurityFilterChain)securityFilterChainBuilder.build();
        securityFilterChains.add(securityFilterChain);
        requestMatcherPrivilegeEvaluatorsEntries.add(this.getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
    }

    if (this.privilegeEvaluator == null) {
        this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(requestMatcherPrivilegeEvaluatorsEntries);
    }

    FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
    if (this.httpFirewall != null) {
        filterChainProxy.setFirewall(this.httpFirewall);
    }

    if (this.requestRejectedHandler != null) {
        filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
    }

    filterChainProxy.afterPropertiesSet();
    Filter result = filterChainProxy;
    if (this.debugEnabled) {
        this.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);
    }

    this.postBuildAction.run();
    return (Filter)result;
}