我们自定义的SecurityConfig都是继承自WebSecurityConfigurerAdapter,今天我们就来看看WebSecurityConfigurerAdapter内部的工作原理以及配置原理。WebSecurityConfigurerAdapter的继承关系图:
在这层继承关系图中,有两个非常重要的类:SecurityBuilder和SecurityConfigurer。
这两个类在之前的深入理解Spring Security配置与构建(源码篇)文章中已经介绍过了,所以关于这两个类就不再赘述,直接从WebSecurityConfigurer类开始。
1、WebSecurityConfigurer
WebSecurityConfigurer其实是一个空接口,只是它里面约束了一些泛型,如下:
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extendsSecurityConfigurer<Filter, T> {}
它里面的泛型很关键,这关乎到WebSecurityConfigurer的目的是啥!
SecurityBuilder中的泛型是Filter,表示SecurityBuilder最终的目的是为了构建一个Filter对象出来。SecurityConfigurer中两个泛型,第一个表示的含义也是SecurityBuilder最终构建的对象。
同时,这里还定义一个新的泛型T,T需要继承自SecurityBuilder,根据WebSecurityConfigurerAdapter中的定义,可以知道T就是SecurityBuilder的子类WebSecurity。
所以,WebSecurityConfigurer的目的可以理解为就是为了配置WebSecurity。
2、WebSecurity
WebSecurity定义如下:
public final class WebSecurity extendsAbstractConfiguredSecurityBuilder<Filter, WebSecurity> implementsSecurityBuilder<Filter>, ApplicationContextAware {}
WebSecurity继承自AbstractConfiguredSecurityBuilder<Filter, WebSecurity>类同时实现了SecurityBuilder接口。
其实在前面的源码介绍中就已经介绍过了,我们再来看看。
2.1、AbstractConfiguredSecurityBuilder
首先,AbstractConfiguredSecurityBuilder中定义了一个枚举类,将整个构建过程分为5种状态,也可以理解为构建过程生命周期的五个阶段,如下:
private enum BuildState {UNBUILT(0),INITIALIZING(1),CONFIGURING(2),BUILDING(3),BUILT(4);private final int order;BuildState(int order) {this.order = order;}public boolean isInitializing() {return INITIALIZING.order == order;}public boolean isConfigured() {return order >= CONFIGURING.order;}}
五种状态分别是UNBUILT``INITIALIZING``CONFIGURING``BUILDING以及BUILT。另外还提供了两个判断方法,isInitializing判断是否正在初始化阶段,isConfigured表示是否已经构建完成。AbstractConfiguredSecurityBuilder中的方法比较多,在这里只列出两个关键的方法:
第一个方法就是这个
add方法,这相当于在收集所有的配置。将所有的xxxConfigurer收集起来存储到configurers中,将来再统一初始化并配置。configurers本身是一个LinkedHashMap,key 是配置类的 class,value 是xxxConfigurer配置类集合。当需要对这些配置类进行集中配置的时候,会通过getConfigurers方法获取配置类集合,这个获取过程就是就是把LinkedHashMap中的 value 拿出来,放到一个集合中返回。private <C extends SecurityConfigurer<O, B>> void add(C configurer) {Assert.notNull(configurer, "configurer cannot be null");Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer.getClass();synchronized (configurers) {if (buildState.isConfigured()) {throw new IllegalStateException("Cannot apply " + configurer+ " to already built object");}List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null;if (configs == null) {configs = new ArrayList<>(1);}configs.add(configurer);this.configurers.put(clazz, configs);if (buildState.isInitializing()) {this.configurersAddedInInitializing.add(configurer);}}}private Collection<SecurityConfigurer<O, B>> getConfigurers() {List<SecurityConfigurer<O, B>> result = new ArrayList<>();for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {result.addAll(configs);}return result;}
另一个方法就是
doBuild方法。在AbstractSecurityBuilder类中,构建的核心逻辑被放到doBuild方法上,但是AbstractSecurityBuilder类中只是定义了抽象的doBuild方法,真正的实现还是在该类中的doBuild方法。@Overrideprotected 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;}}private void init() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {configurer.init((B) this);}for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {configurer.init((B) this);}}private void configure() throws Exception {Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();for (SecurityConfigurer<O, B> configurer : configurers) {configurer.configure((B) this);}}
doBuild方法就是一边更新状态一边进行构建。beforeInit是一个预留方法,没有任何实现;init方法就是找到所有的xxxConfigurer,挨个调用其init方法进行初始化;beforeConfigure是一个预留方法,没有任何实现;configure方法就是找到所有的xxxConfigurer,挨个调用其configure方法进行配置;performBuild方法是真正的过滤器链构建方法,但是在当前AbstractConfiguredSecurityBuilder中的performBuild方法只是一个抽象方法,具体的实现在HttpSecurity类中。2.2、SecurityBuilder
在
HttpSecurity实现SecurityBuilder时,传入的泛型是DefaultSecurityFilterChain,所以SecurityBuilder#build方法的功能就很明确,就是用来构建一个过滤器链出来,但是那个过滤器链是 Spring Security 中的。在WebSecurityConfigurerAdapter中定义的泛型是WebSecurity,所以最终构建的是一个普通的Filter,其实就是FilterChainProxy,关于FilterChainProxy,可以参考Spring Security过滤器架构(源码篇)文章。2.3、WebSecurity
WebSecurity的核心逻辑集中在performBuild构建方法上:@Overrideprotected 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;}
这里的
performBuild方法只有一个功能,那就是构建FilterChainProxy。首先,统计过滤器链的总数,总条数包括两个方面,一个是
ignoredRequests,这是忽略的请求,通过WebSecurity配置的忽略请求;另一个则是securityFilterChainBuilders,也就是通过HttpSecurity配置的过滤器链,有几个算几个。- 创建
securityFilterChains集合,并且遍历上面提到的两种类型的过滤器链,并将过滤器链放入securityFilterChains集合中。 - 在前面的深入理解Spring Security配置与构建(源码篇)文章中介绍过,
HttpSecurity构建出来的过滤器链对象就是DefaultSecurityFilterChain,所以可以直接将build结果放入securityFilterChains集合中,而ignoredRequests中保存时则需要包装一下才可以存入securityFilterChains中。 securityFilterChains集合有数据之后,创建一个FilterChainProxy。- 给新建的
FilterChainProxy配置上防火墙。 - 最后我们返回的就是
FilterChainProxy实例。
🎯从这段分析中,可以看出WebSecurity和HttpSecurity的区别:
HttpSecurity目的是构建过滤器链,一个HttpSecurity对象构建一条过滤器链,一个过滤器链中有N多个过滤器,HttpSecurity所做的事情实际上就是在配置这N个过滤器。WebSecurity目的是构建FilterChainProxy,一个FilterChainProxy中包含有多个过滤器链和一个Firewall。3、WebSecurityConfigurerAdapter
我们最后来看看WebSecurityConfigurerAdapter,由于WebSecurityConfigurer只是一个空接口,WebSecurityConfigurerAdapter就是针对这个空接口提供一个具体的实现,最终目的还是为了方便你配置WebSecurity。WebSecurityConfigurerAdapter中的方法比较多,但是根据我们前面这么文章的分析,重要的方法就两个,一个是init方法,另一个就是configure(WebSecurity web),其他方法都是为这两个方法服务的。
先看init方法: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);});}protected final HttpSecurity getHttp() throws Exception {if (http != null) {return http;}AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);AuthenticationManager authenticationManager = authenticationManager();authenticationBuilder.parentAuthenticationManager(authenticationManager);Map<Class<?>, Object> sharedObjects = createSharedObjects();http = new HttpSecurity(objectPostProcessor, authenticationBuilder,sharedObjects);if (!disableDefaults) {// @formatter:offhttp.csrf().and().addFilter(new WebAsyncManagerIntegrationFilter()).exceptionHandling().and().headers().and().sessionManagement().and().securityContext().and().requestCache().and().anonymous().and().servletApi().and().apply(new DefaultLoginPageConfigurer<>()).and().logout();// @formatter:onClassLoader classLoader = this.context.getClassLoader();List<AbstractHttpConfigurer> defaultHttpConfigurers =SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {http.apply(configurer);}}configure(http);return 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();}
init方法可以算是这里的入口方法了:首先调用getHttp方法进行HttpSecurity的初始化。HttpSecurity的初始化,实际上就是配置了一堆默认的过滤器,配置完成后,最终还是调用了configure(http)方法,该方法又配置了一些过滤器,不过在实际的开发中,我们经常会重写configure(http)方法。HttpSecurity配置完成后,再将HttpSecurity放入WebSecurity的securityFilterChainBuilders集合中。
configure(WebSecurity web)方法实际上是一个空方法,我们在实际开发中可能会重写该方法:
public void configure(WebSecurity web) throws Exception {}
