SpringSecurity启动流程源码解析

前面两期我讲了SpringSecurity认证流程和SpringSecurity鉴权流程,今天是第三期,是SpringSecurity的收尾工作,讲SpringSecurity的启动流程。

就像很多电影拍火了之后其续作往往是前作的前期故事一样,我这个第三期要讲的SpringSecurity启动流程也是不择不扣的"前期故事",它能帮助你真正认清SpringSecurity的整体全貌。

在之前的文章里,在说到SpringSecurity中的过滤器链的时候,往往是把它作为一个概念了解的,就是我们只是知道有这么个东西,也知道它到底是干什么用的,但是我们却不知道这个过滤器链是由什么类什么时候去怎么样创建出来的。

今天这期就是要了解SpringSecurity的自动配置到底帮我们做了什么,它是如何把过滤器链给创建出来的,又是在默认配置的时候怎么加入了我们的自定义配置。

祝有好收获(边赞边看,法力无限)。

  1. 📚EnableWebSecurity
    我们先来看看我们一般是如何使用SpringSecurity的。

我们用SpringSecurity的时候都会先新建一个SpringSecurity相关的配置类,用它继承WebSecurityConfigurerAdapter,然后打上注解@EnableWebSecurity,然后我们就可以通过重写 WebSecurityConfigurerAdapter里面的方法来完成我们自己的自定义配置。

就像这样:

@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {

}

}
我们已经知道,继承WebSecurityConfigurerAdapter是为了重写配置,那这个注解是做了什么呢?

从它的名字@EnableWebSecurity我们可以大概猜出来,它就是那个帮我们自动配置了SpringSecurity的好心人。

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.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;

}
emmm,我猜大家应该有注解相关的知识吧,ok,既然你们都有注解相关的知识,我就直接讲了。

这个@EnableWebSecurity中有两个地方是比较重要的:

一是@Import注解导入了三个类,这三个类中的后两个是SpringSecurity为了兼容性做的一些东西,兼容SpringMVC,兼容SpringSecurityOAuth2,我们主要看的其实是第一个类,导入这个类代表了加载了这个类里面的内容。

二是@EnableGlobalAuthentication这个注解,@EnableWebSecurity大家还没搞明白呢,您这又来一个,这个注解呢,其作用也是加载了一个配置类-AuthenticationConfiguration,看它的名字大家也可应该知道它加载的类是什么相关的了吧,没错就是AuthenticationManager相关的配置类,这个我们可以以后再说。

综上所述,@EnableWebSecurity可以说是帮我们自动加载了两个配置类:WebSecurityConfiguration和AuthenticationConfiguration(@EnableGlobalAuthentication注解加载了这个配置类)。

其中WebSecurityConfiguration是帮助我们建立了过滤器链的配置类,而AuthenticationConfiguration则是为我们注入AuthenticationManager相关的配置类,我们今天主要讲的是WebSecurityConfiguration。

  1. 📖源码概览
    既然讲的是WebSecurityConfiguration,我们照例先把源码给大家看看,精简了一下无关紧要的:

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;

private Boolean debugEnabled;

private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

private ClassLoader beanClassLoader;

@Autowired(required = false)
private ObjectPostProcessor<Object> objectObjectPostProcessor;


@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();
}


@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
        ObjectPostProcessor<Object> objectPostProcessor,
        @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
        throws Exception {
    webSecurity = objectPostProcessor
            .postProcess(new WebSecurity(objectPostProcessor));
    if (debugEnabled != null) {
        webSecurity.debug(debugEnabled);
    }

    webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);

    Integer previousOrder = null;
    Object previousConfig = null;
    for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
        Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
        if (previousOrder != null && previousOrder.equals(order)) {
            throw new IllegalStateException(
                    "@Order on WebSecurityConfigurers must be unique. Order of "
                            + order + " was already used on " + previousConfig + ", so it cannot be used on "
                            + config + " too.");
        }
        previousOrder = order;
        previousConfig = config;
    }
    for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
        webSecurity.apply(webSecurityConfigurer);
    }
    this.webSecurityConfigurers = webSecurityConfigurers;
}

}
如代码所示,首先WebSecurityConfiguration是个配置类,类上面打了@Configuration注解,这个注解的作用大家还知道吧,在这里就是把这个类中所有带@Bean注解的Bean给实例化一下。

这个类里面比较重要的就两个方法:springSecurityFilterChain和setFilterChainProxySecurityConfigurer。

springSecurityFilterChain方法上打了@Bean注解,任谁也能看出来就是这个方法创建了springSecurityFilterChain,但是先别着急,我们不能先看这个方法,虽然它在上面。

  1. 📄SetFilterChainProxySecurityConfigurer
    我们要先看下面的这个方法:setFilterChainProxySecurityConfigurer,为啥呢?

为啥呢?

因为它是@Autowired注解,所以它要比springSecurityFilterChain方法优先执行,从系统加载的顺序来看,我们需要先看它。

@Autowired在这里的作用是为这个方法自动注入所需要的两个参数,我们先来看看这两个参数:

参数objectPostProcessor是为了创建WebSecurity实例而注入进来的,先了解一下即可。

参数webSecurityConfigurers是一个List,它实际上是所有WebSecurityConfigurerAdapter的子类,那如果我们定义了自定义的配置类,其实就是把我们的配置也读取到了。

这里其实有点难懂为什么参数中SecurityConfigurer<Filter, WebSecurity>这个类型可以拿到WebSecurityConfigurerAdapter的子类?

因为WebSecurityConfigurerAdapter实现了WebSecurityConfigurer接口,而WebSecurityConfigurer又继承了SecurityConfigurer<Filter, T>,经过一层实现,一层继承关系之后,WebSecurityConfigurerAdapter终于成为了SecurityConfigurer的子类。

而参数中SecurityConfigurer<Filter, WebSecurity>中的两个泛型参数其实是起到了一个过滤的作用,仔细查看我们的WebSecurityConfigurerAdapter的实现与继承关系,你可以发现我们的WebSecurityConfigurerAdapter正好是这种类型。

ok,说完了参数,我觉得我们可以看看代码了:

@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {

// 创建一个webSecurity实例
webSecurity = objectPostProcessor
        .postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
    webSecurity.debug(debugEnabled);
}

// 根据order排序
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);

Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
    Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
    if (previousOrder != null && previousOrder.equals(order)) {
        throw new IllegalStateException(
                "@Order on WebSecurityConfigurers must be unique. Order of "
                        + order + " was already used on " + previousConfig + ", so it cannot be used on "
                        + config + " too.");
    }
    previousOrder = order;
    previousConfig = config;
}

// 保存配置
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
    webSecurity.apply(webSecurityConfigurer);
}

// 成员变量初始化
this.webSecurityConfigurers = webSecurityConfigurers;

}
根据我们的注释,这段代码做的事情可以分为以为几步:

创建了一个webSecurity实例,并且赋值给成员变量。

紧接着对webSecurityConfigurers通过order进行排序,order是加载顺序。

进行判断是否有相同order的配置类,如果出现将会直接报错。

保存配置,将其放入webSecurity的成员变量中。

大家可以将这些直接理解为成员变量的初始化,和加载我们的配置类配置即可,因为后面的操作都是围绕它初始化的webSecurity实例和我们加载的配置类信息来做的。

这些东西还可以拆出来一步步的来讲,但是这样的话真是一篇文章写不完,我也没有那么大的精力能够事无巨细的写出来,我只挑选这条痕迹清晰的主脉络来讲,如果大家看完能明白它的一个加载顺序其实就挺好了。

就像Spring的面试题会问SpringBean的加载顺序,SpringMVC则会问SpringMVC一个请求的运行过程一样。

全部弄得明明白白,必须要精研源码,在初期,我们只要知道它的一条主脉络,在之后的使用中,哪出了问题你可以直接去定位到可能是哪有问题,这样就已经很好了,学习是一个循环渐进的过程。

  1. 📃SpringSecurityFilterChain
    初始化完变量,加载完配置,我们要开始创建过滤器链了,所以先走setFilterChainProxySecurityConfigurer是有原因的,如果我们不把我们的自定义配置加载进来,创建过滤器链的时候怎么知道哪些过滤器需要哪些过滤器不需要。

@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();
}
springSecurityFilterChain方法逻辑就很简单了,如果我们没加载自定义的配置类,它就替我们加载一个默认的配置类,然后调用这个build方法。

看到这熟悉的方法名称,你就应该知道这是建造者模式,不管它什么模式,既然调用了,我们点进去就是了。

public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException(“This object has already been built”);
}
build()方法是webSecurity的父类AbstractSecurityBuilder中的方法,这个方法又调用了doBuild()方法。

@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;

    // 空方法
    beforeInit();
    // 调用init方法
    init();

    buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;

    // 空方法
    beforeConfigure();
    // 调用configure方法
    configure();

https://giphy.com/channel/i1ha8q
https://giphy.com/channel/rjrgyh
https://giphy.com/channel/wn78w7
https://giphy.com/channel/1it1kd
https://giphy.com/channel/5k3xno
https://giphy.com/channel/79c538
https://giphy.com/channel/5aashy
https://giphy.com/channel/4dfb01
https://giphy.com/channel/9bdaw0
https://giphy.com/channel/55b2h3
https://giphy.com/channel/udpm3g
https://giphy.com/channel/vn4o0g
https://giphy.com/channel/uf8cbs
https://giphy.com/channel/trgzks
https://giphy.com/channel/tjtr48
https://giphy.com/channel/o2v40e
https://giphy.com/channel/5dbvs7
https://giphy.com/channel/91kbmu
https://giphy.com/channel/kxee6v
https://giphy.com/channel/o1g0i8
https://giphy.com/channel/byh54r
https://giphy.com/channel/58tg3o
https://giphy.com/channel/tt25v9
https://giphy.com/channel/z86u7z
https://giphy.com/channel/9dv54o
https://giphy.com/channel/edgyu1
https://giphy.com/channel/aaaklc
https://giphy.com/channel/qown02
https://giphy.com/channel/t4etf2
https://giphy.com/channel/dtexgv
https://giphy.com/channel/b0q2h4
https://giphy.com/channel/5r5r86
https://giphy.com/channel/uoduet
https://giphy.com/channel/e4mpnv
https://giphy.com/channel/rdlirr
https://giphy.com/channel/djbi85
https://giphy.com/channel/b9moom
https://giphy.com/channel/9jlrs9
https://giphy.com/channel/tt7wbt
https://giphy.com/channel/992yrh
https://giphy.com/channel/s81vnm
https://giphy.com/channel/zsrlad
https://giphy.com/channel/9bi8r7
https://giphy.com/channel/9i5126
https://giphy.com/channel/w06sjw
https://giphy.com/channel/8j10c7
https://giphy.com/channel/b35rz9
https://giphy.com/channel/pbpz4r
https://giphy.com/channel/8ii30u
https://giphy.com/channel/w95nmz
https://giphy.com/channel/3sy3gq
https://giphy.com/channel/o40w22
https://giphy.com/channel/q2icuq
https://giphy.com/channel/hr0qss
https://giphy.com/channel/cuf1fw
https://giphy.com/channel/b2sej6
https://giphy.com/channel/3say5f
https://giphy.com/channel/mtrcuv
https://giphy.com/channel/s8lbrv
https://giphy.com/channel/j27l0s
https://giphy.com/channel/6xldue
https://giphy.com/channel/ygrqz2
https://giphy.com/channel/yy4vou
https://giphy.com/channel/n34duv
https://giphy.com/channel/q6ooff
https://giphy.com/channel/zyz7pm
https://giphy.com/channel/mg52df
https://giphy.com/channel/e0qone
https://giphy.com/channel/uy9336
https://giphy.com/channel/0mj6nl
https://giphy.com/channel/v17qq8
https://giphy.com/channel/al3ytt
https://giphy.com/channel/u5llv0
https://giphy.com/channel/6pxpy3
https://giphy.com/channel/2mhn10
https://giphy.com/channel/5qfzj3
https://giphy.com/channel/3ngmw6
https://giphy.com/channel/7egef8
https://giphy.com/channel/iybs9y
https://giphy.com/channel/4penyd
https://giphy.com/channel/f34gmo
https://giphy.com/channel/wu188s
https://giphy.com/channel/ozhmmi
https://giphy.com/channel/hmfm15
https://giphy.com/channel/weunyp
https://giphy.com/channel/d1ch1t
https://giphy.com/channel/4b92hs
https://giphy.com/channel/7ts3j6
https://giphy.com/channel/6lc4kh
https://giphy.com/channel/rk2jh0
https://giphy.com/channel/yiwqn3
https://giphy.com/channel/ub7wnb
https://giphy.com/channel/ddng8m
https://giphy.com/channel/c5nb18
https://giphy.com/channel/y5mxgu
https://giphy.com/channel/9o6ei0
https://giphy.com/channel/of81u5
https://giphy.com/channel/1til2d
https://giphy.com/channel/g4rggi
https://giphy.com/channel/yarrqq
https://giphy.com/channel/8li0qm
https://giphy.com/channel/g5ew1y
https://giphy.com/channel/fyjfr3
https://giphy.com/channel/b778i1
https://giphy.com/channel/bzkijz
https://giphy.com/channel/uujd3q
https://giphy.com/channel/kn7wt7
https://giphy.com/channel/t1ksbc
https://giphy.com/channel/bgrz9h
https://giphy.com/channel/7392i1
https://giphy.com/channel/e9y29d
https://giphy.com/channel/ae7uwe
https://giphy.com/channel/c247kt
https://giphy.com/channel/vy48mm
https://giphy.com/channel/5ncn9m
https://giphy.com/channel/cv4ail
https://giphy.com/channel/9tibl1
https://giphy.com/channel/rq9aeh
https://giphy.com/channel/4cb1xn
https://giphy.com/channel/vpynnf
https://giphy.com/channel/jjz93s
https://giphy.com/channel/gpzx36
https://giphy.com/channel/xsywh7
https://giphy.com/channel/nnac6l
https://giphy.com/channel/ia82yi
https://giphy.com/channel/6pki8s
https://giphy.com/channel/c0lat2
https://giphy.com/channel/rpi2ww
https://giphy.com/channel/iplk9p
https://giphy.com/channel/ftvxne
https://giphy.com/channel/p5ss1j
https://giphy.com/channel/xgysja
https://giphy.com/channel/k7kubi
https://giphy.com/channel/lmeu2v
https://giphy.com/channel/ue4kf3
https://giphy.com/channel/zuru5h
https://giphy.com/channel/bu54lt
https://giphy.com/channel/2gdvou
https://giphy.com/channel/8rirvr
https://giphy.com/channel/91i4f0
https://giphy.com/channel/to5tm5
https://giphy.com/channel/8vllee
https://giphy.com/channel/vs71wn
https://giphy.com/channel/ed7ude
https://giphy.com/channel/65ygsa
https://giphy.com/channel/9rlbl7
https://giphy.com/channel/yfxi3y
https://giphy.com/channel/39hwze
https://giphy.com/channel/2m8qnh
https://giphy.com/channel/5zso6f
https://giphy.com/channel/7md95t
https://giphy.com/channel/q6r66x
https://giphy.com/channel/35t946
https://giphy.com/channel/wnv6fv
https://giphy.com/channel/o5gelm
https://giphy.com/channel/zg3a4j
https://giphy.com/channel/1at9rk
https://giphy.com/channel/ra9skb
https://giphy.com/channel/qxwpwz
https://giphy.com/channel/qrhwf2
https://giphy.com/channel/d01tk4
https://giphy.com/channel/1prweh
https://giphy.com/channel/mm51c4
https://giphy.com/channel/3ilh63
https://giphy.com/channel/lc98u0
https://giphy.com/channel/odecvx
https://giphy.com/channel/hpoaa8
https://giphy.com/channel/08meus
https://giphy.com/channel/wlvnnl
https://giphy.com/channel/396g5w
https://giphy.com/channel/53l6u3
https://giphy.com/channel/t0tudt
https://giphy.com/channel/hpv0nh
https://giphy.com/channel/yuvp7m
https://giphy.com/channel/37jtp3
https://giphy.com/channel/c73nk4
https://giphy.com/channel/fpung4
https://giphy.com/channel/8pf688
https://giphy.com/channel/82rtzs
https://giphy.com/channel/hxhwjp
https://giphy.com/channel/qcqc9p
https://giphy.com/channel/3xhgfw
https://giphy.com/channel/rujm2u
https://giphy.com/channel/egd2we
https://giphy.com/channel/3ye11o
https://giphy.com/channel/695728
https://giphy.com/channel/rrq5bx
https://giphy.com/channel/2938kq
https://giphy.com/channel/qi9ghf
https://giphy.com/channel/hy8vv5
https://giphy.com/channel/hta08j
https://giphy.com/channel/34ogtc
https://giphy.com/channel/izcr43
https://giphy.com/channel/9yohz7

    buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;

    // 调用performBuild
    O result = performBuild();

    buildState = AbstractConfiguredSecurityBuilde
    r.BuildState.BUILT;

    return result;
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值