上篇回顾

前面我们已经分析了Spring Security的核心过滤器FilterChainProxy的创建和运行过程,认识了建造者配置器的作用。

现在我们知道WebSecurity作为一个建造者就是用来创建核心过滤器FilterChainProxy实例的。

WebSecurity在初始化的时候会扫描WebSecurityConfigurerAdapter配置器适配器的子类(即生成HttpSecurity配置器)。

所有的配置器会被调用init();configure();初始化配置,其中生成的每个HttpSecurity配置器都代表了一个过滤器链。

本篇要说的就是HttpSecurity作为一个建造者,是如何去建造出SecurityFilterChain过滤器链实例的!

PS:如果有多个WebSecurityConfigurerAdapter配置器适配器的子类,会产生多个SecurityFilterChain过滤器链实例。Spring Security Oauth2的拓展就是这么做的,有机会再介绍

spring security 怎么创建的过滤器

我们已经知道了springSecurityFilterChain(类型为FilterChainProxy)是实际起作用的过滤器链,DelegatingFilterProxy起到代理作用。

我们创建的MySecurityConfig继承了WebSecurityConfigurerAdapterWebSecurityConfigurerAdapter就是用来创建过滤器链,重写的configure(HttpSecurity http)的方法就是用来配置HttpSecurity的。

protected void configure(HttpSecurity http) throws Exception {
        http
            .requestMatchers() // 指定当前`SecurityFilterChain`实例匹配哪些请求
                .anyRequest().and()
            .authorizeRequests() // 拦截请求,创建FilterSecurityInterceptor
                .anyRequest().authenticated() // 在创建过滤器的基础上的一些自定义配置
                .and() // 用and来表示配置过滤器结束,以便进行下一个过滤器的创建和配置
            .formLogin().and() // 设置表单登录,创建UsernamePasswordAuthenticationFilter
            .httpBasic(); // basic验证,创建BasicAuthenticationFilter
}

上面的configure(HttpSecurity http)方法内的配置最终内容主要是Filter的创建。

http.authorizeRequests()http.formLogin()http.httpBasic()分别创建了ExpressionUrlAuthorizationConfigurerFormLoginConfigurerHttpBasicConfigurer。在三个类从父级一直往上找,会发现它们都是SecurityConfigurer建造器的子类。SecurityConfigurer中又有configure()方法。该方法被子类实现就用于创建各个过滤器,并将过滤器添加进HttpSecurity中维护的装有Filter的List中,比如HttpBasicConfigurer中的configure方法,源码如下:

HttpSecurity作为建造者会把根据api把这些配置器添加到实例中

HttpBasicConfigurer

getOrApply

apply

这些配置器中大都是创建了相应的过滤器,并进行配置,最终在HttpSecurity建造SecurityFilterChain实例时放入过滤器链

configure()

HttpSecurity 初始化流程

先来看看 doBuild的调用流程,再看看这些方法的最终实现方式。

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

WebSecurityConfigurerAdapter是最终我们需要手动配置的安全配置器,先来看看WebSecurityConfigurerAdapter中重要的实现

  • init()方法传入 WebSecurity:
    public void init(final WebSecurity web) throws Exception {
     final HttpSecurity http = getHttp();
     web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
        FilterSecurityInterceptor securityInterceptor = http
              .getSharedObject(FilterSecurityInterceptor.class);
        web.securityInterceptor(securityInterceptor);
     });
    }
    
    • init()方法有调用getHttp() ,getHttp()里面调用configure(HttpSecurity http),在init()http加入 securityFilterChainBuilders
    public WebSecurity addSecurityFilterChainBuilder(
                SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
            this.securityFilterChainBuilders.add(securityFilterChainBuilder);
            return this;
        }
    
    • configure(HttpSecurity http) 父类中有默认的时效方法,所以你不覆盖就会有默认的认证方式
    protected void configure(HttpSecurity http) throws Exception {
        logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
    
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin().and()
            .httpBasic();
    }
    
  • configure() 方法:AbstractConfiguredSecurityBuilder 中调用configure()
    configurer.configure(WebSecurity);
    

    将 WebSecurity 传入 WebSecurityConfigurerAdapterconfigure(WebSecurity http)方法

  • performBuild()

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<>(
                chainSize);
        for (RequestMatcher ignoredRequest : ignoredRequests) {
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
        }
        for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
            securityFilterChains.add(securityFilterChainBuilder.build());
        }
        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;
    }

最终由

for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
            securityFilterChains.add(securityFilterChainBuilder.build());
        }

加入securityFilterChains