与你若只如初见,何须感伤离别。
一、HttpSecurity
1.1 概述
HttpSecurity这个bean我们前面的章节有提到过,它是Spring Security各种配置的基础,这个bean是Spring Security自动加载的,并且同时会默认帮我们完成了Spring Security完整功能的各项配置,我们先找到这个bean加载的位置:
1.2 httpSecurity默认配置
我们一点一点来看看,Spring Security都帮我们怎么进行了HttpSecurity的默认配置了:
-
首先初始化了AuthenticationManagerBuilder,这个对象上一章节我们已经看过了,这里再注意一下,我们初始化的authenticationManager时放到哪里的:
httpSecurity是重新new了一个authenticationManagerBuilder的,并且我们初始化的authenticationManager是放在builder对象里的parentAuthenticationManager属性里的。 -
然后我们看看httpSecurity的构造方法:
这里注意createSharedObjects()这个方法,从名字上我们可以大概看出来这是创建了一个共享对象的容器:
是个Map对象,key是个类对象,value是实例对象,初始化的时候放入了spring上下文对象,然后继续往下看httpSecurity的构造方法:
这里有个setSharedObject方法,这个方法同样的意思:
往httpSecurity的sharedObjects属性里放入共享对象,这里注意这个属性,后面会频繁的出现,另外注意这里requestMatcherConfigurer这个属性也进行了初始化。
- 接下来就是使用httpSecurity进行了默认配置:
我们后面自己配置的时候也是采用的这种方式,我们先随便看一个:
这里先新建了一个CsrfConfigurer对象,然后传入getOrApply方法:
这里实际上就是将上面new的configure放入了configurers属性中了。
- 上面看了一种配置方式,是新建configure并放入httpSecurity的configures属性中,下面再看一种配置方式:
这里出现了两个httpSecurity的变量,filterOrders和filters,从名字上看,我们应该知道,这就跟我们Spring Security过滤器息息相关了,我们来分别看一下这两个属性:
filter变量就是存储过滤器的,filterOrders是存储过滤器顺序的,所以Spring Security里面的过滤器是有顺序的,而且是通过这个变量来决定的。到这里httpSecurity初始化完成了,同时也帮助我们完成了默认的配置。
总结一下:
httpSecurity默认初始化会出事后一个authenticationManager对象并放入一个authenticationManagerBuilder对象中,随后这个builder对象会放入httpSecurity对象的sharedObjects属性中;
然后httpSecurity会创建类如csrf,exceptionHandling等等的配置器,随后也放入sharedObjects属性中。
1.3 初始化SecurityFilterChain
- 先回忆一下Spring Security的过滤器叫FilterChainProxy,其中存储过滤器的属性叫filterChains,集合里元素的类型时SecurityFilterChain:
因此,我们前面做了那么多,肯定是为了这个SecurityFilterChain来做准备的,下面我们看 SpringBootWebSecurityConfiguration 这个类:
前面一样的意思,大家自己进去也可以发现,是往httpSecurity的sharedObjects中放入配置器,我们往下看httpSecurity的build方法:
跟到这里有没有发现很熟悉,我们回忆一下authenticationManager的初始化是不是也是调用的这个方法:
从这个类结构图中我们不难发现,HttpSecurity和AuthenticationManagerBuilder都是这个builder类的子类,所以说实现的逻辑都是一样的,但是,我们HttpSecurity的构建就要复杂一点了,前面我们知道了,httpSecurity初始化的时候创建了很多configure并且最终是放入到他的map集合中的,在这里我们可以看到:
- init方法和configure方法都是遍历的configurers然后执行每一个configure的配置方法的,我们挑一个配置器看一下:
我们看下前面的formLogin()方法,这个方法里面是创建了一个FormLoginConfigurer配置器,我们看一下这个配置器的配置,先看下构造方方法:
这里我们看到了一个非常熟悉的过滤器:UsernamePasswordAuthenticationFilter,再看下init方法:
由于目前我们没有做任何额外的配置,因此这里的DefaultLoginPageGeneratingFilter过滤器也初始化了,再看下configure方法,注意一下,这里configure方法是在FormLoginConfigurer配置器的父类中实现的:
注意可以看到这里有我们前面提到的getSharedObject方法,就是从我们前面setSharedObject放入的容器中再拿出来的,这里可以重点关注一下从sharedObjects中拿出了我们已经初始化的authenticationManager,这里是不是有个疑问,明明我们前面set进去的是builder对象,这里这么直接get了manager出来,回到前面我们httpSecurity的build方法:
这里开始的时候肯定是没有manager的,因此又通过builder执行了build方法,这个build方法我们前面已经详细的看过了,这里就不多说了,结果是这里经过build方法创建了manager,并且放入了sharedObject中了。回到FormLoginConfigurer的配置方法,我们可以看到最后执行了httpSecurity的addFilter方法,这个方法我们前面也看过了,将过滤器放入httpSecurity的filters属性集合中了。完成了各种预初始化,初始化,预配置,配置方法后,最后来到了performBuild执行构建方法:
这里我们看到,先把filters里面的过滤器排序,遍历然后放入到sortedFilters,最后创建了一个DefaultSecurityFilterChain对象,至此,一个SecurityFilterChain创建完毕,这里构造方法里还有个requestMatcher属性留意一下,其实就是Spring Security的FilterChainProxy中的filterChains是一个集合,所以支持多个,那当有多个SecurityFilterChain时,一个请求进来会使用哪个SecurityFilterChain就是由这个requestMatcher决定的:
- 那么FilterChainsProxy是从哪里来的呢,我们看下面的代码:
上面我们看的哪个代码结构图,webSecurity和httpSecurity也是同一个父类,因此webSecurity的build方法也是同样的逻辑,直接看下webSecurity的performBuild方法:
至此,FilterChainsProxy也创建完毕了。
总结:
Spring Security的初始化过程比较复杂,但是值得我们借鉴,各种配置器,建造器如何统一处理,对于逻辑相似的处理过程,我们可以通过接口,抽象类,统一入口,个性化处理逻辑我们在具体实现类中实现具体处理逻辑。
未完待续~~