过滤器链分析 提起Spring Security的实现原理,很多读者都会想到过滤器链。因为Spring Security中的所有功能都是通过过滤器来实现的,这些过滤器组成一个完整的过滤器链。那么,这些过滤器 链是如何初始化的?我们前面反复提到的AuthenticationManager又是如何初始化的?通过前面章节的学习,相信读者己经有了一些认识,本章我们将从头开始,分析Spring Security的初始化流程,同时再通过六个案例来让读者深入理解并且学会如何制作过滤器链。由于初始化流程相对复杂,因此我们没有选择在一开始就讲解Spring Security初始化流程,而是放到本节。当读者对于Spring Security有一个基本的认知之后再来讲解,此时相对来说就会比较容易理解。
本章涉及的主要知识点有:
初始化流程分析。 ObjectPostProcessor 的使用。 多种用户定义方式。 定义多个过滤器链。 静态资源过滤。 使用JSON格式登录。 添加登录验证码。 1. 初始化流程分析 Spring Security初始化流程整体上来说理解起来并不难,但是这里涉及许多零碎的知识点, 把这些零碎的知识点搞懂了,再来梳理初始化流程就会容易很多。因此,这里先介绍一下SpringSecurity中一些常见的关键组件,在理解这些组件的基础上,再来分析初始化流程,就能加深对其的理解。
1.1 ObjectPostProcessor ObjectPostProcessor是Spring Security中使用频率最高的组件之一,它是一个对象后置处理器,也就是当一个对象创建成功后,如果还有一些额外的事情需要补充,那么可以通过 ObjectPostProcessor来进行处理。这个接口中默认只有一个方法postProcess,该方法用来完成对对象的二次处理,代码如下:
public interface ObjectPostProcessor <T> { <O extends T > O postProcess (O object) ; }
ObjectPostProcessor默认有两个继承类,如图4-1所示。
图 4-1 AutowireBeanFactoryObjectPostProcessor:由于 Spring Security 中大量采用了 Java 配置, 许多过滤器都是直接new出来的,这些直接new出来的对象并不会自动注入到Spring 容器中。Spring Security这样做的本意是为了简化配置,但是却带来了另外一个问题就是, 大量new出来的对象需要我们手动注册到Spring客器中去。AutowireBeanFactoryObjectPostProcessor对象所承担的就是这件事,一个对象new出来之后,只要调用 AutowireBeanFactoryObjectPostProcessor.postProcess 方法,就可以成功注入到 Spring 容器中,它的实现原理就是通过调用Spring容器中的AutowireCapableBeanFactory对象将一个new出来的对象注入到Spring容器中去。 CompositeObjectPostProcessor:这是ObjectPostProcessor 的另一个实现,一个对象可以有一个后置处理器,开发者也可以自定义多个对象后置处理器。 CompositeObjectPostProcessor是一个组合的对象后置处理器,它里边维护了一个List集合,集合中存放了某一个对象的所有后置处理器,当需要执行对象的后置处理器时,会遍历集合中的所有ObjectPostProcessor实例,分别调用实例的postProcess方法进行对象后置处理。在Spring Security框架中,最终使用的对象后置处理器其实就是 CompositeObjectPostProcessor ,它里边的集合默认只有一个对象,就是 AutowireBeanFactoryObjectPostProcessor。 在Spring Security中,开发者可以灵活地配置项目中需要哪些Spring Security过滤器,一 旦选定过滤器之后,每一个过滤器都会有一个对应的配置器,叫作xxxConfigurer (例如CorsConfigurer. CsrfConfigurer等),过滤器都是在 xxxConfigurer 中 new 出来的,然后在 postProcess方法中处理一遍,就将这些过滤器注入到Spring容器中了。这是对象后置处理器ObjectPostProcessor的主要作用。
1.2 SecurityFilterChain 从名称上可以看出,SecurityFilterChain就是Spring Security中的过滤器链对象。下面来看一下 SecurityFilterChain的源码:
public interface SecurityFilterChain { boolean matches (httpsServletRequest request) ; List<Filter> getFilters () ; }
可以看到,SecurityFilterChain中有两个方法:
matches:该方法用来判断request请求是否应该被当前过滤器链所处理心 getFilters:该方法返回一个List集合,集合中存放的就是Spring Security中的过滤器。换言之,如果matches方法返回true,那么request请求就会在getFilters方法所返回的Filter 集合中被处理。 SecurityFilterChain只有一个默认的实现类就是DefaultSecurityFilterChain,其中定义了两 个属性,并具体实现了 SecurityFilterChain中的两个方法:
查看代码
public final class DefaultSecurityFilterChain implements SecurityFilterChain { private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class); private final RequestMatcher requestMatcher; private final List<Filter> filters; public DefaultSecurityFilterChain (RequestMatcher requestMatcher, Filter... filters) { this (requestMatcher, Arrays.asList(filters)); } public DefaultSecurityFilterChain (RequestMatcher requestMatcher, List<Filter> filters) { logger.info("Creating filter chain: " + requestMatcher + ", " + filters); this .requestMatcher = requestMatcher; this .filters = new ArrayList <>(filters); } public RequestMatcher getRequestMatcher () { return requestMatcher; } public List<Filter> getFilters () { return filters; } public boolean matches (httpsServletRequest request) { return requestMatcher.matches(request); } @Override public String toString () { return "[ " + requestMatcher + ", " + filters + "]" ; } }
可以看到,在DefaultSecurityFilterChain的构造方法中,需要传入两个对象,一个是请求 匹配器requestMatcher,另一个则是过滤器集合或者过滤器数组filters。这个实现类比较简单, 这里就不再赘述了。
需要注意的是,在一个Spring Security项目中,SecurityFilterChain的实例可能会有多个,在后面的小节中会详细分析,并演示多个SecurityFilterChain实例的情况。
1.3 SecurityBuilder Spring Security中所有需要构建的对象都可以通过SecurityBuilder来实现,默认的过滤器 链、代理过滤器AuthenticationManager 等,都可以通过 SecurityBuilder来构建。SecurityBuilder的实现类如图4-2所示。
图 4-2 SecurityBuilder:
我们先来看SecurityBuilder的源码:
public interface SecurityBuilder <O> { O build () throws Exception; }
由上述代码可以看到,SecurityBuilder中只有一个build方法,就是对象构建方法。build 方法的返回值,就是具体构建的对象泛型O,也就是说不同的SecurityBuilder将来会构建出不同的对象。
httpsSecurityBuiIder:
httpsSecurityBuilder是用来构建 httpsSecurity对象的,httpsSecurityBuilder 的定义如下:
public interface httpsSecurityBuilder <H extends httpsSecurityBuilder <H>> extends SecurityBuilder <DefaultSecurityFilterChain> { <C extends SecurityConfigurer <DefaultSecurityFilterChain, H>> C getConfigurer ( Class<C> clazz) ; <C extends SecurityConfigurer <DefaultSecurityFilterChain, H>> C removeConfigurer ( Class<C> clazz) ; <C> void setSharedObject (Class<C> sharedType, C object) ; <C> C getSharedObject (Class<C> sharedType) ; H authenticationProvider (AuthenticationProvider authenticationProvider) ; H userDetailsService (UserDetailsService userDetailsService) throws Exception; H addFilterAfter (Filter filter, Class<? extends Filter> afterFilter) ; H addFilterBefore (Filter filter, Class<? extends Filter> beforeFilter) ; H addFilter (Filter filter) ; }
我们简单分析一下这段源码:
httpsSecurityBuilder对象本身在定义时就有一个泛型,这个泛型是httpsSecurityBuilder 的子类,由于默认情况下httpsSecurityBuilder的实现类只有一个httpsSecurity,所以可以暂且把接口中的H都当成httpsSecurity来理解。 httpsSecurityBuilder 继承自 SecurityBuilder 接口,同时也指定了 SecurityBuilder 中的泛型为DefaultSecurityFilterChain,也就是说,httpsSecurityBuilder最终想要构建的对象是 DefaultSecurityFilterChain。 getConfigurer方法用来获取一个配置器,所谓的配置器就是xxxConfigurer,我们将在下一小节中详细介绍配置器, removeConfigurer方法用来移除一个配置器(相当于从Spring Security过滤器链中移 除一个过滤器)。 setSharedObject/getSharedObject这两个方法用来设置或者获取一个可以在多个配置器之间共享的对象。 authenticationProvider 方法可以用来配置一个认证器 AuthenticationProvider。 userDetailsService 方法可以用来配置一个数据源 UserDetailsService。 addFilterAfter/addFilterBefore方法表示在某一个过滤器之后或者之前添加一个自定义的过滤器。 addFilter方法可以添加一个过滤器,这个过滤器必须是Spring Security框架提供的 过滤器的一个实例或者其扩展,添加完成后,会自动进行过滤器的排序。 AbstractSecurityBuilder:
AbstractSecurityBuilder实现了 SecurityBuilder 接口,并对 build 做了完善,确保只 build 一次。我们来看—下 AbstractSecurityBuilder 源码:
public abstract class AbstractSecurityBuilder <O> implements SecurityBuilder <O> { private AtomicBoolean building = new AtomicBoolean (); private O object; 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" ); } public final O getObject () { if (!this .building.get()) { throw new IllegalStateException ("This object has not been built" ); } return this .object; } protected abstract O doBuild () throws Exception; }
由上述代码可以看到,在AbstractSecurityBuilder类中:
首先声明了 building变量,可以确保即使在多线程环境下,配置类也只构建一次。 对build方法进行重写,并II设置为final,这样在AbstractSecurityBuilder的了类中 将不能再次重写build方法,在build方法内部,通过building变量来控制配置类只构建一次, 具体的构建工作则交给doBuild方法去完成。 getObject方法用来返回构建的对象。 doBuild方法则是具体的构建方法,该方法在AbstractSecurityBuilder中是一个抽象方法,具体的实现在其子类中。 一言以蔽之,AbstractSecurityBuilder的作用是确保目标对象只被构建一次。
AbstractConfiguredSecurityBuilder:
AbstractConfiguredSecurityBuilder类的源码就稍微长一点,我们分别来看,首先在AbstractConfiguredSecurityBuilder中声明了一个枚举类,用来描述构建过程的不同状态:
private static 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:构建完成。 这个枚举类里边还提供了两个判断方法 islnitializing 表示是否正在初始化中, isConfigured 方法表示是否已完成配置。
AbstractConfiguredSecurityBuilder中还声明了 configurers变量,用来保存所有的配置类。针对configurers变量,我们可以进行添加配置、移除配置等操作,相关方法如下:
查看代码
public abstract class AbstractConfiguredSecurityBuilder <O, B extends SecurityBuilder <O>> extends AbstractSecurityBuilder <O> { private final Log logger = LogFactory.getLog(getClass()); private final LinkedHashMap<Class<? extends SecurityConfigurer <O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap <Class<? extends SecurityConfigurer <O, B>>, List<SecurityConfigurer<O, B>>>(); private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList <SecurityConfigurer<O, B>>(); private final Map<Class<? extends Object >, Object> sharedObjects = new HashMap <Class<? extends Object >, Object>(); private final boolean allowConfigurersOfSameType; private BuildState buildState = BuildState.UNBUILT; private ObjectPostProcessor<Object> objectPostProcessor; protected AbstractConfiguredSecurityBuilder ( ObjectPostProcessor<Object> objectPostProcessor) { this (objectPostProcessor, false ); } protected AbstractConfiguredSecurityBuilder ( ObjectPostProcessor<Object> objectPostProcessor, boolean allowConfigurersOfSameType) { Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null" ); this .objectPostProcessor = objectPostProcessor; this .allowConfigurersOfSameType = allowConfigurersOfSameType; } public O getOrBuild () { if (isUnbuilt()) { try { return build(); } catch (Exception e) { logger.debug("Failed to perform build. Returning null" , e); return null ; } } else { return getObject(); } } @SuppressWarnings("unchecked") public <C extends SecurityConfigurerAdapter <O, B>> C apply (C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this ); add(configurer); return configurer; } public <C extends SecurityConfigurer <O, B>> C apply (C configurer) throws Exception { add(configurer); return configurer; } @SuppressWarnings("unchecked") public <C> void setSharedObject (Class<C> sharedType, C object) { this .sharedObjects.put(sharedType, object); } @SuppressWarnings("unchecked") public <C> C getSharedObject (Class<C> sharedType) { return (C) this .sharedObjects.get(sharedType); } public Map<Class<? extends Object >, Object> getSharedObjects() { return Collections.unmodifiableMap(this .sharedObjects); } @SuppressWarnings("unchecked") private <C extends SecurityConfigurer <O, B>> void add (C configurer) throws Exception { 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 <SecurityConfigurer<O, B>>(1 ); } configs.add(configurer); this .configurers.put(clazz, configs); if (buildState.isInitializing()) { this .configurersAddedInInitializing.add(configurer); } } } @SuppressWarnings("unchecked") public <C extends SecurityConfigurer <O, B>> List<C> getConfigurers (Class<C> clazz) { List<C> configs = (List<C>) this .configurers.get(clazz); if (configs == null ) { return new ArrayList <>(); } return new ArrayList <>(configs); } @SuppressWarnings("unchecked") public <C extends SecurityConfigurer <O, B>> List<C> removeConfigurers (Class<C> clazz) { List<C> configs = (List<C>) this .configurers.remove(clazz); if (configs == null ) { return new ArrayList <>(); } return new ArrayList <>(configs); } @SuppressWarnings("unchecked") public <C extends SecurityConfigurer <O, B>> C getConfigurer (Class<C> clazz) { List<SecurityConfigurer<O, B>> configs = this .configurers.get(clazz); if (configs == null ) { return null ; } if (configs.size() != 1 ) { throw new IllegalStateException ("Only one configurer expected for type " + clazz + ", but got " + configs); } return (C) configs.get(0 ); } @SuppressWarnings("unchecked") public <C extends SecurityConfigurer <O, B>> C removeConfigurer (Class<C> clazz) { List<SecurityConfigurer<O, B>> configs = this .configurers.remove(clazz); if (configs == null ) { return null ; } if (configs.size() != 1 ) { throw new IllegalStateException ("Only one configurer expected for type " + clazz + ", but got " + configs); } return (C) configs.get(0 ); } @SuppressWarnings("unchecked") public O objectPostProcessor (ObjectPostProcessor<Object> objectPostProcessor) { Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null" ); this .objectPostProcessor = objectPostProcessor; return (O) this ; } protected <P> P postProcess (P object) { return this .objectPostProcessor.postProcess(object); } }
我们解析一下这段源码:
首先声明了一个configurers变量,用来保存所有的配置类,key是配置类Class对象, 值是一个List集合中放着配置类。 apply方法有两个,参数类型略有差异,主要功能基本一致,都是向configurers变量中添加配置类,具体的添加过程则是调用add方法。 add方法用来将所有的配置类保存到configurers中,在添加的过程中,如果 allowConfigurersOfSameType变量为true,则表示允许相同类型的配置类存在,也就是List集合中可以存在多个相同类型的配置类。默认情况下,如果是普通配置类, allowConfigurersOfSameType是false,所以List集合中的配置类始终只有一个配置类;如果在 AuthenticationManagerBuilder 中设置 allowConfigurersOfSameType 为 true,此时相同类型的配置类可以有多个(下文会详细分析AuthenticationManagerBuilder)。 getConfigurers(Class)方法可以从configurers中返回某一个配置类对应的所有实例 removeConfigurers方法可以从configurers中移除某一个配置类对应的所有实例,并返回被移除掉的配置类实例集合, getConfigurer方法也是获取配置类实例,但是只获取集合中第一项。 removeConfigurer方法可以从configurers中移除某一个配置类对应的所有配置类实例,并返回被移除掉的配置类实例中的第一项。 getConfigurers方法是一个私有方法,主要是把所有的配置类实例放到一个集合中返 回.在配置类初始化和配置的时候,会调用到该方法, 这些就是 AbstractConfiguredSecurityBuilder 中关于 configurers 的所有操作。
接下来就是AbstractConfiguredSecurityBuilder中的doBuild方法了,这是核心的构建方法。
查看代码
@Override 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; } } protected void beforeInit () throws Exception {} protected void beforeConfigure () throws Exception {} protected abstract O performBuild () throws Exception;@SuppressWarnings("unchecked") 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 ); } } @SuppressWarnings("unchecked") private void configure () throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.configure((B) this ); } } private Collection<SecurityConfigurer<O, B>> getConfigurers () { List<SecurityConfigurer<O, B>> result = new ArrayList <SecurityConfigurer<O, B>>(); for (List<SecurityConfigurer<O, B>> configs : this .configurers.values()) { result.addAll(configs); } return result; } private boolean isUnbuilt () { synchronized (configurers) { return buildState == BuildState.UNBUILT; } }
在doBuild方法中,一边更新构建状态,一边执行构建方法由构建方法中,beforeInit 是一个空的初始化方法,如果需要在初始化之前做一些准备工作,可以通过重写该方法实现,
init方法是所有配置类的初始化方法,在该方法中,遍历所有的配置类,并调用其 init方法完成初始化操作。
beforeConfigure方法可以在configure方法执行之前做一些准备操作心该方法默认也是一个空方法,
configure方法用来完成所有配置类的配置,在configure方法中,遍历所有的配置类,分别调用其configure方法完成配置。
performBuild方法用来做最终的构建操作,前面的准备工作完成后,最后在 performBuild方法中完成构建,这是一个抽象方法,具体的实现则在不同的配置类中。
这些就是AbstractConfiguredSecurityBuilder中最主要的几个方法,其他一些方法比较简单,这里就不一一赘述了
ProviderManagerBuilder:
ProviderManagerBuilder继承自 SecurityBuilder接口,并制定了构建的对象是 AuthenticationManager, 代码如下:
public interface ProviderManagerBuilder <B extends ProviderManagerBuilder <B>> extends SecurityBuilder <AuthenticationManager> { B authenticationProvider (AuthenticationProvider authenticationProvider) ; }
可以看到,ProviderManagerBuilder 中增加 了一个 authenticationProvider 方法,同时通过泛型指定了构建的对象为AuthenticationManager。
AuthenticationManagerBuilder:
AuthenticationManagerBuilder 用来构建 AuthenticationManager 对象,它继承自 AbstractConfiguredSecurityBuilder,并且实现了 ProviderManagerBuilder接口,源码比较长,我们截取部分常用代码,代码如下:
查看代码
public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuilder <AuthenticationManager, AuthenticationManagerBuilder> implements ProviderManagerBuilder <AuthenticationManagerBuilder> { private final Log logger = LogFactory.getLog(getClass()); private AuthenticationManager parentAuthenticationManager; private List<AuthenticationProvider> authenticationProviders = new ArrayList <>(); private UserDetailsService defaultUserDetailsService; private Boolean eraseCredentials; private AuthenticationEventPublisher eventPublisher; public AuthenticationManagerBuilder (ObjectPostProcessor<Object> objectPostProcessor) { super (objectPostProcessor, true ); } public AuthenticationManagerBuilder parentAuthenticationManager ( AuthenticationManager authenticationManager) { if (authenticationManager instanceof ProviderManager) { eraseCredentials(((ProviderManager) authenticationManager) .isEraseCredentialsAfterAuthentication()); } this .parentAuthenticationManager = authenticationManager; return this ; } public AuthenticationManagerBuilder authenticationEventPublisher ( AuthenticationEventPublisher eventPublisher) { Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null" ); this .eventPublisher = eventPublisher; return this ; } public AuthenticationManagerBuilder eraseCredentials (boolean eraseCredentials) { this .eraseCredentials = eraseCredentials; return this ; } public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication () throws Exception { return apply(new InMemoryUserDetailsManagerConfigurer <>()); } public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication () throws Exception { return apply(new JdbcUserDetailsManagerConfigurer <>()); } public <T extends UserDetailsService > DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService ( T userDetailsService) throws Exception { this .defaultUserDetailsService = userDetailsService; return apply(new DaoAuthenticationConfigurer <>( userDetailsService)); } public LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthentication () throws Exception { return apply(new LdapAuthenticationProviderConfigurer <>()); } public AuthenticationManagerBuilder authenticationProvider ( AuthenticationProvider authenticationProvider) { this .authenticationProviders.add(authenticationProvider); return this ; } @Override protected ProviderManager performBuild () throws Exception { if (!isConfigured()) { logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null." ); return null ; } ProviderManager providerManager = new ProviderManager (authenticationProviders, parentAuthenticationManager); if (eraseCredentials != null ) { providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials); } if (eventPublisher != null ) { providerManager.setAuthenticationEventPublisher(eventPublisher); } providerManager = postProcess(providerManager); return providerManager; } public boolean isConfigured () { return !authenticationProviders.isEmpty() || parentAuthenticationManager != null ; } public UserDetailsService getDefaultUserDetailsService () { return this .defaultUserDetailsService; } private <C extends UserDetailsAwareConfigurer <AuthenticationManagerBuilder, ? extends UserDetailsService >> C apply ( C configurer) throws Exception { this .defaultUserDetailsService = configurer.getUserDetailsService(); return (C) super .apply(configurer); } }
首先在AuthenticationManagerBuilder的构造方法中,调用了父类的构造方法,注意第二个参数传递了true,表示允许相同类型的配置类同时存在(结合 AbstractConfiguredSecurityBuilder 的源码来理解) parentAuthenticationManager 方法用来给一个 AuthenticationManager 设置 parents inMemoryAuthentication方法用来配置基于内存的数据源,该方法会自动创建 InMemoryUserDetailsManagerConfigurer配置类,并最终将该配置类添加到父类的configurers 变量中。由于设置了允许相同类型的配置类同时存在,因此inMemoryAuthentication方法可以反复调用多次 jdbcAuthentication 以及 userDetailsService 方法与 inMemoryAuthentication 方法类似, 也是用来配置数据源的,这里不再赘述。 authenticationProvider 方法用来向 authenticationProviders 集合中添加 AuthenticationProvider对象,根据前面第3节的介绍,我们己经知道一个AuthenticationManager实例中包含多个 AuthenticationProvider 实例,那么多个 AuthenticationProvider 实例可以通过 authenticationProvider方法进行添加。 performBuild方法则执行具体的构建工作,常用的AuthenticationManager实例就是 ProviderManager,所以这里创建 ProviderManager 对象,并旦配置 authenticationProviders 和 parentAuthenticationManager对象,ProviderManager对象创建成功之后,再去对象后置处理器中处理一遍再返回。 这就是AuthenticationManagerBuilder中的一个大致逻辑。
httpsSecurity:
httpsSecurity的主要作用是用来构建一条过滤器链,并反映到代码上,也就是构建一个 DefaultSecurityFilterChain 对象,一个 DefaultSecurityFilterChain 对象包含一个路径匹配器和多个Spring Security 过滤器,httpsSecurity 中通过收集各种各样的 xxxConfigurer,将 Spring Security 过滤器对应的配置类收集起来,并保存到父类AbstractConfiguredSecurityBuilder的configurers 变量中,在后续的构建过程中,再将这些xxxConfigurer构建为具体的Spring Security过滤器, 同时添加到httpsSecurity的filters对象中。
由于httpsSecurity中存在大量功能类似的方法,因此这里挑选一个作为例子用来说明httpsSecurity的配置原理,代码如下:
查看代码
public final class httpsSecurity extends AbstractConfiguredSecurityBuilder <DefaultSecurityFilterChain, httpsSecurity> implements SecurityBuilder <DefaultSecurityFilterChain>, httpsSecurityBuilder<httpsSecurity> { private final RequestMatcherConfigurer requestMatcherConfigurer; private List<Filter> filters = new ArrayList <>(); private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE; private FilterComparator comparator = new FilterComparator (); @SuppressWarnings("unchecked") public httpsSecurity (ObjectPostProcessor<Object> objectPostProcessor, AuthenticationManagerBuilder authenticationBuilder, Map<Class<? extends Object>, Object> sharedObjects) { super (objectPostProcessor); Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null" ); setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder); for (Map.Entry<Class<? extends Object >, Object> entry : sharedObjects .entrySet()) { setSharedObject((Class<Object>) entry.getKey(), entry.getValue()); } ApplicationContext context = (ApplicationContext) sharedObjects .get(ApplicationContext.class); this .requestMatcherConfigurer = new RequestMatcherConfigurer (context); } private ApplicationContext getContext () { return getSharedObject(ApplicationContext.class); } public OpenIDLoginConfigurer<httpsSecurity> openidLogin () throws Exception { return getOrApply(new OpenIDLoginConfigurer <>()); } public HeadersConfigurer<httpsSecurity> headers () throws Exception { return getOrApply(new HeadersConfigurer <>()); } public CorsConfigurer<httpsSecurity> cors () throws Exception { return getOrApply(new CorsConfigurer <>()); } public SessionManagementConfigurer<httpsSecurity> sessionManagement () throws Exception { return getOrApply(new SessionManagementConfigurer <>()); } public PortMapperConfigurer<httpsSecurity> portMapper () throws Exception { return getOrApply(new PortMapperConfigurer <>()); } public JeeConfigurer<httpsSecurity> jee () throws Exception { return getOrApply(new JeeConfigurer <>()); } public X509Configurer<httpsSecurity> x509 () throws Exception { return getOrApply(new X509Configurer <>()); } public RememberMeConfigurer<httpsSecurity> rememberMe () throws Exception { return getOrApply(new RememberMeConfigurer <>()); } public ExpressionUrlAuthorizationConfigurer<httpsSecurity>.ExpressionInterceptUrlRegistry authorizeRequests () throws Exception { ApplicationContext context = getContext(); return getOrApply(new ExpressionUrlAuthorizationConfigurer <>(context)) .getRegistry(); } public RequestCacheConfigurer<httpsSecurity> requestCache () throws Exception { return getOrApply(new RequestCacheConfigurer <>()); } public ExceptionHandlingConfigurer<httpsSecurity> exceptionHandling () throws Exception { return getOrApply(new ExceptionHandlingConfigurer <>()); } public SecurityContextConfigurer<httpsSecurity> securityContext () throws Exception { return getOrApply(new SecurityContextConfigurer <>()); } public ServletApiConfigurer<httpsSecurity> servletApi () throws Exception { return getOrApply(new ServletApiConfigurer <>()); } public CsrfConfigurer<httpsSecurity> csrf () throws Exception { ApplicationContext context = getContext(); return getOrApply(new CsrfConfigurer <>(context)); } public LogoutConfigurer<httpsSecurity> logout () throws Exception { return getOrApply(new LogoutConfigurer <>()); } public AnonymousConfigurer<httpsSecurity> anonymous () throws Exception { return getOrApply(new AnonymousConfigurer <>()); } public FormLoginConfigurer<httpsSecurity> formLogin () throws Exception { return getOrApply(new FormLoginConfigurer <>()); } public OAuth2LoginConfigurer<httpsSecurity> oauth2Login () throws Exception { return getOrApply(new OAuth2LoginConfigurer <>()); } public ChannelSecurityConfigurer<httpsSecurity>.ChannelRequestMatcherRegistry requiresChannel () throws Exception { ApplicationContext context = getContext(); return getOrApply(new ChannelSecurityConfigurer <>(context)) .getRegistry(); } public httpsBasicConfigurer<httpsSecurity> httpsBasic () throws Exception { return getOrApply(new httpsBasicConfigurer <>()); } public <C> void setSharedObject (Class<C> sharedType, C object) { super .setSharedObject(sharedType, object); } @Override protected void beforeConfigure () throws Exception { setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build()); } @Override protected DefaultSecurityFilterChain performBuild () throws Exception { Collections.sort(filters, comparator); return new DefaultSecurityFilterChain (requestMatcher, filters); } public httpsSecurity authenticationProvider ( AuthenticationProvider authenticationProvider) { getAuthenticationRegistry().authenticationProvider(authenticationProvider); return this ; } public httpsSecurity userDetailsService (UserDetailsService userDetailsService) throws Exception { getAuthenticationRegistry().userDetailsService(userDetailsService); return this ; } private AuthenticationManagerBuilder getAuthenticationRegistry () { return getSharedObject(AuthenticationManagerBuilder.class); } public httpsSecurity addFilterAfter (Filter filter, Class<? extends Filter> afterFilter) { comparator.registerAfter(filter.getClass(), afterFilter); return addFilter(filter); } public httpsSecurity addFilterBefore (Filter filter, Class<? extends Filter> beforeFilter) { comparator.registerBefore(filter.getClass(), beforeFilter); return addFilter(filter); } public httpsSecurity addFilter (Filter filter) { Class<? extends Filter > filterClass = filter.getClass(); if (!comparator.isRegistered(filterClass)) { throw new IllegalArgumentException ( "The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead." ); } this .filters.add(filter); return this ; } public httpsSecurity addFilterAt (Filter filter, Class<? extends Filter> atFilter) { this .comparator.registerAt(filter.getClass(), atFilter); return addFilter(filter); } public RequestMatcherConfigurer requestMatchers () { return requestMatcherConfigurer; } public httpsSecurity requestMatcher (RequestMatcher requestMatcher) { this .requestMatcher = requestMatcher; return this ; } public httpsSecurity antMatcher (String antPattern) { return requestMatcher(new AntPathRequestMatcher (antPattern)); } public httpsSecurity mvcMatcher (String mvcPattern) { HandlerMappingIntrospector introspector = new HandlerMappingIntrospector (getContext()); return requestMatcher(new MvcRequestMatcher (introspector, mvcPattern)); } public httpsSecurity regexMatcher (String pattern) { return requestMatcher(new RegexRequestMatcher (pattern, null )); } public final class MvcMatchersRequestMatcherConfigurer extends RequestMatcherConfigurer { private MvcMatchersRequestMatcherConfigurer (ApplicationContext context, List<MvcRequestMatcher> matchers) { super (context); this .matchers = new ArrayList <>(matchers); } public RequestMatcherConfigurer servletPath (String servletPath) { for (RequestMatcher matcher : this .matchers) { ((MvcRequestMatcher) matcher).setServletPath(servletPath); } return this ; } } public class RequestMatcherConfigurer extends AbstractRequestMatcherRegistry <RequestMatcherConfigurer> { protected List<RequestMatcher> matchers = new ArrayList <>(); private RequestMatcherConfigurer (ApplicationContext context) { setApplicationContext(context); } @Override public MvcMatchersRequestMatcherConfigurer mvcMatchers (httpsMethod method, String... mvcPatterns) { List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns); setMatchers(mvcMatchers); return new MvcMatchersRequestMatcherConfigurer (getContext(), mvcMatchers); } @Override public MvcMatchersRequestMatcherConfigurer mvcMatchers (String... patterns) { return mvcMatchers(null , patterns); } @Override protected RequestMatcherConfigurer chainRequestMatchers ( List<RequestMatcher> requestMatchers) { setMatchers(requestMatchers); return this ; } private void setMatchers (List<? extends RequestMatcher> requestMatchers) { this .matchers.addAll(requestMatchers); requestMatcher(new OrRequestMatcher (this .matchers)); } public httpsSecurity and () { return httpsSecurity.this ; } } @SuppressWarnings("unchecked") private <C extends SecurityConfigurerAdapter <DefaultSecurityFilterChain, httpsSecurity>> C getOrApply ( C configurer) throws Exception { C existingConfig = (C) getConfigurer(configurer.getClass()); if (existingConfig != null ) { return existingConfig; } return apply(configurer); } }
以form表单登录配置为例,在httpsSecurity中有两个重载方法可以进行配置:第一个是一个无参的formLogin方法,该方法的返回值是一个FormLoginConfigurer对象,开发者可以在该对象的基础上继续完善对form表单的配置,我们在前面章节中配置的表单登录都是通过这种方式来进行配置的。第二个是一个有参的formLogin方法,该方法的参数是一个FormLoginConfigurer对象,返回值则是一个httpsSecurity对象,也就是说开发者可以提前在外面配置好FormLoginConfigurer对象,然后直接传进来进行配置即可,返回值 httpsSecurity对象则可以在方法返回后直接进行其他过滤器的配置。无论是有参还是无参,最终都会调用到getOrApply方法,该方法会调用父类的getConfigurer方法去查看是否己经有对应的配置类了,如果有,则直接返回;如果没有,则调用apply方法添加到父类的configurer变量中 httpsSecurity 中其他过滤器的配置都和form表单登录配置类似,这里就不再赘述了。 每一套过滤器链都会有一个AuthenticationManager对象来进行认证操作(如果认证失败,则会调用AuthenticationManager的parent再次进行认证),主要是通过authenticationProvider方法配置执行认证的authenticationProvider对象,通过userDetailsService方法配置 UserDetailsService,最后在 beforeConfigure 方法中触发 AuthenticationManager 对象的构建。 performBuild方法则是进行DefaultSecurityFilterChain对象的构建,传入请求匹配器和过滤器集合filters,在构建之前,会先按照既定的顺序对filters进行排序。 通过addFilterAfter、addFilterBefore两个方法,我们可以在某一个过滤器之后或者之前添加一个自定义的过滤器(该方法巳在httpsSecurityBuilder中声明,此处是具体实现)。 addFilter方法可以向过滤器链中添加一个过滤器,这个过滤器必须是Spring Security 框架提供的过滤器的一个实例或者其扩展,实际上,在每一个xxxConfigurer的configure方法中,都会调用addFilter方法将构建好的过滤器添加到httpsSecurity中的filters集合中(addFilter 方法已在httpsSecurityBuilder中声明,此处是具体实现)。 addFilterAt方法可以在指定位置添加一个过滤器。需要注意的是,在同一个位置添加多个过滤器并不会覆盖现有的过滤器。 这便是httpsSecurity的基本功能。
WebSecurity:
相比于httpsSecurity, WebSecurity是在一个更大的层面上去构建过滤器。一个httpsSecurity 对象可以构建一个过滤器链,也就是一个DefaultSecurityFilterChain对象,而一个项目中可以存在多个httpsSecurity对象,也就可以构建多个DefaultSecurityFilterChain过滤器链。
WebSecurity 负责将 httpsSecurity 所构建的 DefaultSecurityFilterChain 对象(可能有多个), 以及其他一些需要忽略的请求,再次重新构建为一个FilterChainProxy对象,同时添加上https 防火墙,
我们来看一下WebSecurity中的几个关键方法:
查看代码
public final class WebSecurity extends AbstractConfiguredSecurityBuilder <Filter, WebSecurity> implements SecurityBuilder <Filter>, ApplicationContextAware { private final Log logger = LogFactory.getLog(getClass()); private final List<RequestMatcher> ignoredRequests = new ArrayList <>(); private final List<SecurityBuilder<? extends SecurityFilterChain >> securityFilterChainBuilders = new ArrayList <SecurityBuilder<? extends SecurityFilterChain >>(); private IgnoredRequestConfigurer ignoredRequestRegistry; private FilterSecurityInterceptor filterSecurityInterceptor; private httpsFirewall httpsFirewall; private boolean debugEnabled; private WebInvocationPrivilegeEvaluator privilegeEvaluator; private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler (); private SecurityExpressionHandler<FilterInvocation> expressionHandler = defaultWebSecurityExpressionHandler; private Runnable postBuildAction = new Runnable () { public void run () { } }; public WebSecurity (ObjectPostProcessor<Object> objectPostProcessor) { super (objectPostProcessor); } public IgnoredRequestConfigurer ignoring () { return ignoredRequestRegistry; } public WebSecurity httpsFirewall (httpsFirewall httpsFirewall) { this .httpsFirewall = httpsFirewall; return this ; } public WebSecurity debug (boolean debugEnabled) { this .debugEnabled = debugEnabled; return this ; } public WebSecurity addSecurityFilterChainBuilder ( SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) { this .securityFilterChainBuilders.add(securityFilterChainBuilder); return this ; } public WebSecurity privilegeEvaluator ( WebInvocationPrivilegeEvaluator privilegeEvaluator) { this .privilegeEvaluator = privilegeEvaluator; return this ; } public WebSecurity expressionHandler ( SecurityExpressionHandler<FilterInvocation> expressionHandler) { Assert.notNull(expressionHandler, "expressionHandler cannot be null" ); this .expressionHandler = expressionHandler; return this ; } public SecurityExpressionHandler<FilterInvocation> getExpressionHandler () { return expressionHandler; } public WebInvocationPrivilegeEvaluator getPrivilegeEvaluator () { if (privilegeEvaluator != null ) { return privilegeEvaluator; } return filterSecurityInterceptor == null ? null : new DefaultWebInvocationPrivilegeEvaluator (filterSecurityInterceptor); } public WebSecurity securityInterceptor (FilterSecurityInterceptor securityInterceptor) { this .filterSecurityInterceptor = securityInterceptor; return this ; } public WebSecurity postBuildAction (Runnable postBuildAction) { this .postBuildAction = postBuildAction; return this ; } @Override 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 (httpsFirewall != null ) { filterChainProxy.setFirewall(httpsFirewall); } 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; } public final class MvcMatchersIgnoredRequestConfigurer extends IgnoredRequestConfigurer { private final List<MvcRequestMatcher> mvcMatchers; private MvcMatchersIgnoredRequestConfigurer (ApplicationContext context, List<MvcRequestMatcher> mvcMatchers) { super (context); this .mvcMatchers = mvcMatchers; } public IgnoredRequestConfigurer servletPath (String servletPath) { for (MvcRequestMatcher matcher : this .mvcMatchers) { matcher.setServletPath(servletPath); } return this ; } } public class IgnoredRequestConfigurer extends AbstractRequestMatcherRegistry <IgnoredRequestConfigurer> { private IgnoredRequestConfigurer (ApplicationContext context) { setApplicationContext(context); } @Override public MvcMatchersIgnoredRequestConfigurer mvcMatchers (httpsMethod method, String... mvcPatterns) { List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns); WebSecurity.this .ignoredRequests.addAll(mvcMatchers); return new MvcMatchersIgnoredRequestConfigurer (getApplicationContext(), mvcMatchers); } @Override public MvcMatchersIgnoredRequestConfigurer mvcMatchers (String... mvcPatterns) { return mvcMatchers(null , mvcPatterns); } @Override protected IgnoredRequestConfigurer chainRequestMatchers ( List<RequestMatcher> requestMatchers) { WebSecurity.this .ignoredRequests.addAll(requestMatchers); return this ; } public WebSecurity and () { return WebSecurity.this ; } } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .defaultWebSecurityExpressionHandler .setApplicationContext(applicationContext); this .ignoredRequestRegistry = new IgnoredRequestConfigurer (applicationContext); } }
首先在WebSecurity中声明了 ignoredRequests集合,这个集合中保存了所有被忽略的请求,因为在实际项目中,并非所有的请求都需要经过Spring Security过滤器链,有一些静态资源可能不需要权限认证,直接返回给客户端即可,那么这些需要忽略的请求可以直接保存 在 ignoredRequests 变量中。 接下来声明了一个securityFilterChainBuilders集合,该集合用来保存所有的 httpsSecurity 对象,每一个 httpsSecurity 对象创建成功之后,通过 addSecurityFliterChainBuilder 方法将 httpsSecurity 对象添加到 securityFilterChainBuilders 集合中。 httpsFirewall方法可以用来配置请求防火墙,关于请求防火墙,我们会在后面的章节中专门讲解。 performBuild方法则是具体的构建方法,在该方法中,首先统计出过滤器链的总个数(被忽略的请求个数+通过httpsSecurity创建出来的过滤器链个数),然后创建一个集合 securityFilterChains,遍历被忽略的请求并分别构建成DefaultSecurityFilterChain对象保存到 securityFilterChains集合中。需要注意的是,对于被忽略的请求,在构建DefaultSecurityFilterChain对象时,只是传入了请求匹配器,而没有传入对应的过滤器链,这就意味着这些被忽略掉的请求,将来不必经过Spring Security过滤器链;接下来再遍历securityFilterChainBuilders集合,调用每个对象的build方法构建DefaultSecurityFilterChain并存入securityFilterChains集合中,然后传入securityFilterChains集合构建FilterChainProxy对象,最后再设置https 防火墙。所有设置完成之后,最后返回filterChainProxy对象。 FilterChainProxy就是我们最终构建出来的代理过滤器链,通过Spring提供的DelegatingFilterProxy将FilterChainProxy对象嵌入到WebFilter中(原生过滤器链中)。
读者可以回忆一下前面我们绘制的FilterChainProxy架构图,对照着来理解上面的源码应该就很容易了,如图4-3所示。
至此,关于SecurityBuilder体系中的几个关键类就介绍完了 ,至于httpsSecurity和 WebSecurity是怎么配置到一起的,我们将在后面的章节中进行分析。
图 4-3 1.4 FilterChainProxy FilterChainProxy 通过 DelegatingFilterProxy 代理过滤器被集成到 WebFilter 中, DelegatingFilterProxy作为一个代理对象,相信很多读者可能都用过(例如在Spring中整合Shiro 就会用到),它不承载具体的业务。所以,Spring Security中的过滤器链的最终执行,就是在FilterChainProxy中,因此这里也 来分析一下FilterChainProxy的源码。
FilterChainProxy的源码比较长,我们一段一段来看:
private List<SecurityFilterChain> filterChains;private FilterChainValidator filterChainValidator = new NullFilterChainValidator ();private httpsFirewall firewall = new StricthttpsFirewall ();public FilterChainProxy () {} public FilterChainProxy (SecurityFilterChain chain) { this (Arrays.asList(chain)); } public FilterChainProxy (List<SecurityFilterChain> filterChains) { this .filterChains = filterChains; }
首先声明了三个变量:
由于在Spring Security中可以同时存在多个过滤器链,filterchains就是用来保存过滤器链的,注意保存的是过滤器链,而不是一个个具体的过滤器, FilterChainValidator是一个过滤器链配置完成后的验证器,默认使用 NullFilterChainValidator其实没有做任何验证。 创建了一个默认的防火墙对象firewall。 在构造方法中传入过滤器链的集合,并赋值给filterChains变量。
由于FilterChainProxy本质上就是一个过滤器,因此它的核心方法就是doFilter方法,接下来我们来看一下doFilter方法:
@Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean clearContext = request.getAttribute(FILTER_APPLIED) == null ; if (clearContext) { try { request.setAttribute(FILTER_APPLIED, Boolean.TRUE); doFilterInternal(request, response, chain); } finally { SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } } else { doFilterInternal(request, response, chain); } }
doFilter方法相当于是整个Spring Security过滤器链的入口,我们在前面章节中所涉及的 一些具体的过滤器如SecurityContextPersistenceFilter,都是在该doFilter方法之后执行的。作 为整个过滤器链的入口,这里多了一个clearContext变量,如果是第一次执行该doFilter方法, 执行完成后,在finally代码块中需要从SecurityContextHolder里清除用户信息,这个主要是为了防止用户没有正确配置SecurityContextPersistenceFilter,从而导致登录用户信息没有被正确清除,进而发生内存泄漏。
在doFilter方法中,过滤器的具体执行则交给了 doFilterlnternal方法:
private void doFilterInternal (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException FirewalledRequest fwRequest = firewall.getFirewalledRequest((httpsServletRequest) request); httpsServletResponse fwResponse = firewall.getFirewalledResponse((httpsServletResponse) response); List<Filter> filters = getFilters(fwRequest); if (filters == null || filters.size() == 0 ) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list" )); } fwRequest.reset(); chain.doFilter(fwRequest, fwResponse); return ; } VirtualFilterChain vfc = new VirtualFilterChain (fwRequest, chain, filters); vfc.doFilter(fwRequest, fwResponse); }
在doFilterlnternal方法中,首先会将request对象转换为一个FirewalledRequest对象,这个转换过程会进行https防火墙处理(https防火墙将在后面详细介绍),同时将response对象也转为httpsServletResponse。接下来调用getFilters方法获取当前请求对应的过滤器链, getFilters方法会遍历filterchains集合,进而判断出当前请求和哪一个过滤器链是对应的,如果找到的过滤器链filters为null,或者filters中没有元素,说明当前请求并不需要经过SpringSecurity过滤器链,此时执行Request.reset方法对https防火墙中的属性进行重置,再执行 chain.doFilter方法,回到WebFilter中,Spring Security过滤器链将被跳过(回忆上一小结 WebSecurity中配置的忽略请求)。如果filters集合中是有元素的,也就是说当前请求需要经过filters集合中元素所构成的过滤器链,那么构建一个虚拟的过滤器链对象VirtualFilterChain, 并执行其doFilter方法。
private static class VirtualFilterChain implements FilterChain { private final FilterChain originalChain; private final List<Filter> additionalFilters; private final FirewalledRequest firewalledRequest; private final int size; private int currentPosition = 0 ; private VirtualFilterChain (FirewalledRequest firewalledRequest, FilterChain chain, List<Filter> additionalFilters) { this .originalChain = chain; this .additionalFilters = additionalFilters; this .size = additionalFilters.size(); this .firewalledRequest = firewalledRequest; } @Override public void doFilter (ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentPosition == size) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " reached end of additional filter chain; proceeding with original chain" ); } this .firewalledRequest.reset(); originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1 ); if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " at position " + currentPosition + " of " + size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'" ); } nextFilter.doFilter(request, response, this ); } } }
VirtualFilterChain中首先声明了五个变量:
originalChain:表示原生的过滤器链,执行它的doFilter方法会回到WebFilter中。 additionalFilters:这个List集合中存储的Filter就是本次请求的Filter。 firewalledRequest:当前请求对象。 size:过滤器链的大小。 currentPosition:过滤器链执行的下标。 在VirtualFilterChain的构造方法中,会给相应的变量赋值。
在doFilter方法中,会首先判断当前执行的下标是否等于过滤器链的大小,如果相等,则说明整个过滤器链中的所有过滤器都已经挨个走一遍了,此时先对https防火墙中的属性进行重置,然后调用。originalChain.doFilter 方法跳出Spring Security Filter,回到 WebFilter;如果不相等,则currentPosition自增,然后从过滤器链集合中取出一个过滤器去执行,注意执行的时候第三个参数this表示当前对象(即VirtualFilterChain),这样在每一个过滤器执行完之后, 最后的chain.doFilter方法又会回到当前doFilter方法中,继续下一个过滤器的调用.
这就是FilterChainProxy的一个大致工作原理。
SecurityConfigurer中有两个核心方法,一个是init方法,用来完成配置类的初始化操作, 另外一个是configure方法,进行配置类的配置。上一小结介绍的 AbstractConfiguredSecurityBuilder类,里边的init方法和configure其实就是在遍历执行不同配 置类的init和configure方法。
SecurityConfigurer的实现类比较多,这里主要梳理一下常见的SecurityConfigurer实现类, 我们分别来看一下。
SecurityConfigurer:
先来看源码,代码如下:
public interface SecurityConfigurer <O, B extends SecurityBuilder <O>> { void init (B builder) throws Exception; void configure (B builder) throws Exception; }
可以看到,SecurityConfigurer只有两个方法:init和configure,两个方法的参数都是 SecurityBuilder对象,也就是说在这两个方法中对SecurityBuilder进行初始化和配置。
SecurityConfigurer的类非常多,因为每一个过滤器都有自己对应的xxxConfigiuer,这 里着重介绍几个关键的实现类,如图4-4所示。
图 4-4 我们分别来看这几个实现类:
SecurityConfigurerAdapter
SecurityConfigurerAdapter 实现了 SecurityConfigurer 接口,它的源码如下:
查看代码
public abstract class SecurityConfigurerAdapter <O, B extends SecurityBuilder <O>> implements SecurityConfigurer <O, B> { private B securityBuilder; private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor (); public void init (B builder) throws Exception { } public void configure (B builder) throws Exception { } public B and () { return getBuilder(); } protected final B getBuilder () { if (securityBuilder == null ) { throw new IllegalStateException ("securityBuilder cannot be null" ); } return securityBuilder; } @SuppressWarnings("unchecked") protected <T> T postProcess (T object) { return (T) this .objectPostProcessor.postProcess(object); } public void addObjectPostProcessor (ObjectPostProcessor<?> objectPostProcessor) { this .objectPostProcessor.addObjectPostProcessor(objectPostProcessor); } public void setBuilder (B builder) { this .securityBuilder = builder; } private static final class CompositeObjectPostProcessor implements ObjectPostProcessor <Object> { private List<ObjectPostProcessor<? extends Object >> postProcessors = new ArrayList <ObjectPostProcessor<?>>(); @SuppressWarnings({ "rawtypes", "unchecked" }) public Object postProcess (Object object) { for (ObjectPostProcessor opp : postProcessors) { Class<?> oppClass = opp.getClass(); Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class); if (oppType == null || oppType.isAssignableFrom(object.getClass())) { object = opp.postProcess(object); } } return object; } private boolean addObjectPostProcessor ( ObjectPostProcessor<? extends Object> objectPostProcessor) { boolean result = this .postProcessors.add(objectPostProcessor); Collections.sort(postProcessors, AnnotationAwareOrderComparator.INSTANCE); return result; } } }
从这段源码中,我们可以分析出SecurityConfigurerAdapter主要做了如下几件事:
提供了一个SecurityBuilder对象,为每一个配置类都提供一个SecurityBuilder对象, 将来通过SecurityBuilder构建出具体的配置对象;通过and方法返回SecurityBuilder对象,这样方便不同的配置类在配置时,可以进行链式配置(第2章中我们在定义SecurityConfig时所使用的and方法)。 定义了内部类CompositeObjectPostProcessor这是一个复合的对象后置处理器。 提供了一个addObjectPostProcessor方法,通过该方法可以向复合的对象后置处理器中添加新的ObjectPostProcessor实例。 这是SecurityConfigurerAdapter提供的主要功能。
UserDetailsAwareConfigurer:
UserDetailsAwareConfigurer的子类主要负责配置用户认证相关的组件,如UserDetailsService 等,UserDetailsAwareConfigurer 中提供了获取 UserDetailsService 的抽象方法,具体实现则在它的子类中,UserDetailsAwareConfigurer的子类如图4-5所示。
图 4-5 AbstractDaoAuthenticationConfigurer: 完成时 DaoAuthenticationProvider 的配置。 UserDetailsServiceConfigurer:完成对 UserDetailsService 的配置。 UserDetailsManagerConfigurer :使用 UserDetailsManager 构建用户 对象,完成对 AuthenticationManagerBuilder 的填充。 JdbcUserDetailsManagerConfigurer:配置 JdbcUserDetailsManager 并填充到 AuthenticationManagerBuilder InMemoryUserDetailsManagerConfigurer:配置 InMemoryUserDetailsManager。 DaoAuthenticationConfigurer: 完成对 DaoAuthenticationProvider 的配置。 AbstracthttpsConfigurer:
AbstracthttpsConfigurer主要是为了给在httpsSecurity中使用的配置类添加一个方便的父类,提取岀共同的操作。
public abstract class AbstracthttpsConfigurer <T extends AbstracthttpsConfigurer <T, B>, B extends httpsSecurityBuilder <B>> extends SecurityConfigurerAdapter <DefaultSecurityFilterChain, B> { @SuppressWarnings("unchecked") public B disable () { getBuilder().removeConfigurer(getClass()); return getBuilder(); } @SuppressWarnings("unchecked") public T withObjectPostProcessor (ObjectPostProcessor<?> objectPostProcessor) { addObjectPostProcessor(objectPostProcessor); return (T) this ; } }
可以看到,提取出来的方法其实就两个:一个disable表示禁用某一个配置(前面章节中我们配置的.csrf().disable。),本质上就是从构建器的configurers集合中移除某一个配置类,这样在将来构建的时候就不存在该配置类,那么对应的功能也就不存在(被禁用);另一个 withObjectPostProcessor表示给某一个对象添加一个对象后置处理器,由于该方法的返回值是当前对象,所以该方法可以用在链式配置中。
AbstracthttpsConfigurer的实现类比较多,基本上都用来配置务种各样的过滤器,参见表 4-1。
表4-1 GlobalAuthenticationConfigurerAdapter:
GlobalAuthenticationConfigurerAdapter 主要用于配置全局 AuthenticationManagerBuilder, 在 AuthenticationConfiguration 类中会自动使用 GlobalAuthenticationConfigurerAdapter 提供的 Bean 来配置全局 AuthenticationManagerBuilder。
在第3章介绍ProviderManager时曾经提到过,默认情况下ProviderManager有一个parent, 这个parent就是通过这里的全局AuthenticationManagerBuilder来构建的。
GlobalAuthenticationConfigurerAdapter 有四个不同的子类,如图 4-6 所示。
图 4-6 InitializeAuthenticationProviderBeanManagerConfigurer: 初始化全局的 AuthenticationProvider对象。 InitializeAuthenticationProviderManagerConfigurer:配置全局的 AuthenticationProvider对象,配置过程就是从Spring容器中查找 AuthenticationProvider并设置给全局的 AuthenticationManagerBuilder 对象。 InitializeUserDetailsBeanManagerConfigurer:初始化全局的 UserDetailsService 对象。 InitializeUserDetailsManagerConfigurer:配置全局的 UserDetailsService 对象,配置过程就是从 Spring 容器中查找 UserDetailsService,并设置给全局的 AuthenticationManagerBuilder 对象。 EnableGlobalAuthenticationAutowiredConfigurer:从 Spring 容器中加载被@EnableGlobalAuthentication注解标记的Bean。 WebSecurityConfigurer:
WebSecurityConfigurer是一个空接口,我们可以通过它来自定义 WebSecurity。 WebSecurityConfigurer 只有一个实现类就是 WebSecurityConfigurerAdapter,在大多数情况下, 升发者通过继承WebSecurityConfigurerAdapter来实现对WebSecurity的自定义配置。
WebSecurityConfigurerAdapter:
WebSecurityConfigurerAdapter 是一个可以方便创建 WebSecurityConfigurer 实例的基类, 开发者可以通过覆盖 WebSecurityConfigurerAdapter中的方法完成对httpsSecurity和 WebSecurity的定制,在前面的章节中,我们所定制的Spring Security登录都是通过自定义类继承 WebSecurityConfigurerAdapter来实现的。
在 WebSecurityConfiginerAdapter 中声明了 两个 AuthenticationManagerBuilder 对象用来构 建 AuthenticationManager:
private AuthenticationManagerBuilder authenticationBuilder;
private AuthenticationManagerBuilder localConfigureAuthenticationBuilder;
其中,localConfigureAuthenticationBuilder对象负责构建全局的 AuthenticationManager,而 authenticationBuilder 则负责构建局部的 AutlienticationManager。 局部的 AuthenticationManager 是和每一个httpsSecurity对象绑定的,而全局的AuthenticationManager对象则是所有局部AuthenticationManager 的 parent。需要注意的足,localConfigureAuthenticationBuilder并非总是有用,在开发者没有重写configure(AuthenticationManagerBuilder)方法的情况下,全局的 AuthenticationManager 对象是由 Authenticationconfiguration 类中的 getAuthenticationManager 方法提供的,如果用户重写了 configure(AuthenticationManagerBuilder)方法,则全局的 AuthenticationManager 就由 localConfigureAuthenticationBuilder负责构建。这里可能会感觉有点绕,在后面的小节中,我们将通过实际的例子展示全局AuthenticationManager对象的构建。
WebSecurityConfigurerAdapter 类的初始化方法如下:
查看代码
protected WebSecurityConfigurerAdapter (boolean disableDefaults) { this .logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class); this .contentNegotiationStrategy = new HeaderContentNegotiationStrategy (); this .objectPostProcessor = new ObjectPostProcessor <Object>() { public <T> T postProcess (T object) { throw new IllegalStateException (ObjectPostProcessor.class.getName() + " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration" ); } }; this .trustResolver = new AuthenticationTrustResolverImpl (); this .disableDefaults = disableDefaults; } protected void configure (AuthenticationManagerBuilder auth) throws Exception { this .disableLocalConfigureAuthenticationBldr = true ; } protected final httpsSecurity gethttps () throws Exception { if (this .https != null ) { return this .https; } else { AuthenticationEventPublisher eventPublisher = this .getAuthenticationEventPublisher(); this .localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = this .authenticationManager(); this .authenticationBuilder.parentAuthenticationManager(authenticationManager); Map<Class<?>, Object> sharedObjects = this .createSharedObjects(); this .https = new httpsSecurity (this .objectPostProcessor, this .authenticationBuilder, sharedObjects); if (!this .disableDefaults) { ((httpsSecurity)((DefaultLoginPageConfigurer)((httpsSecurity)((httpsSecurity)((httpsSecurity)((httpsSecurity)((httpsSecurity)((httpsSecurity)((httpsSecurity)((httpsSecurity)this .https.csrf().and()).addFilter(new WebAsyncManagerIntegrationFilter ()).exceptionHandling().and()).headers().and()).sessionManagement().and()).securityContext().and()).requestCache().and()).anonymous().and()).servletApi().and()).apply(new DefaultLoginPageConfigurer ())).and()).logout(); ClassLoader classLoader = this .context.getClassLoader(); List<AbstracthttpsConfigurer> defaulthttpsConfigurers = SpringFactoriesLoader.loadFactories(AbstracthttpsConfigurer.class, classLoader); Iterator var6 = defaulthttpsConfigurers.iterator(); while (var6.hasNext()) { AbstracthttpsConfigurer configurer = (AbstracthttpsConfigurer)var6.next(); this .https.apply(configurer); } } this .configure(this .https); return this .https; } } public AuthenticationManager authenticationManagerBean () throws Exception { return new WebSecurityConfigurerAdapter .AuthenticationManagerDelegator(this .authenticationBuilder, this .context); } protected AuthenticationManager authenticationManager () throws Exception { if (!this .authenticationManagerInitialized) { this .configure(this .localConfigureAuthenticationBldr); if (this .disableLocalConfigureAuthenticationBldr) { this .authenticationManager = this .authenticationConfiguration.getAuthenticationManager(); } else { this .authenticationManager = (AuthenticationManager)this .localConfigureAuthenticationBldr.build(); } this .authenticationManagerInitialized = true ; } return this .authenticationManager; } public void init (WebSecurity web) throws Exception { httpsSecurity https = this .gethttps(); web.addSecurityFilterChainBuilder(https).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)https.getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); } public void configure (WebSecurity web) throws Exception {} protected void configure (httpsSecurity https) throws Exception { this .logger.debug("Using default configure(httpsSecurity). If subclassed this will potentially override subclass configure(httpsSecurity)." ); ((httpsSecurity)((httpsSecurity)((AuthorizedUrl)https.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpsBasic(); }
在init方法中,首先调用gethttps方法获取一个httpsSecurity实例,并将获取到的实例添加到WebSecurity对象中,再由WebSecurity对象进行构建。 在gethttps方法中,如果https对象已经初始化,则直接返回,否则进行初始化操作。 在初始化的过程中,给localConfigureAuthenticationBuilder设置事件发布器,并调用 authenticationManager 方法获取全局的 AuthenticationManager 对象。 在 authenticationManager 方法中,如果全局的 AuthenticationManager 对象还没有初始化,则先调用configure方法,该方法的逻辑很简单,就是将disableLocalConfigureAuthenticationBIdr变量:由 false 变为 true,接下来就会进入到 authenticationManager 方法的 if 分支中,通 过调用 authenticationConfiguration.getAuthenticationManager()方法获取全局的 AuthenticationManager 对象并返回也如果开发者自己重写了 configure(AuthenticationManager Builder)方法,则 disableLocalConfigureAuthenticationBldr 变量就一直是 false,没有机会变为 true,这样就会进入到else分支中,通过localConfignreAuthenticationBldr变量来构建 authenticationManager 对象。 再次回到gethttps方法中,获取到全局的authenticationManager对象之后,设置给 autheuticationBuilder,然后创建一个httpsSecurity实例岀来,并为其配置上默认的过滤器。默认的配置完成后,调用configure(httpsSecurity)方法进行扩展配置,WebSecurityConfigurer Adapter中对configure(httpsSecurity)方法提供了默认的实现,开发者也可以自定义该方法。 这就是 WebSecurityConfigurerAdapter的初始化方法,其实就是创建并配置一个 httpsSecurity实例,之后添加到WebSecurity中。
WebSecurityConfigurerAdapter中的configure方法是一个空方法,可以用来配置 WebSecurity,代码如下:
public void configure(WebSecurity web) throws Exception {
}
一般来说,如果我们有一些静态资源不需要经过Spring Security过滤器,就可以通过重写该方法实现。
至此,在Spring Security初始化过程中,几个重要的组件都介绍完了,单纯的源码读者看起来可能会比较枯燥,在后面的小节中,我会结合大量的应用案例,来帮助大家深入理解源码。
不过在讲解具体的案例之前,我们还是先来分析一遍Sprmg Security的初始化流程,将前面讲的这些知识点串起来。
1.6 初始化流程分析 在Spring Boot中使用Spring Security,初始化就从Spring Security的自动化配置类中开始:
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(DefaultAuthenticationEventPublisher.class) @EnableConfigurationProperties(SecurityProperties.class) @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class }) public class SecurityAutoConfiguration { @Bean @ConditionalOnMissingBean(AuthenticationEventPublisher.class) public DefaultAuthenticationEventPublisher authenticationEventPublisher (ApplicationEventPublisher publisher) { return new DefaultAuthenticationEventPublisher (publisher); } }
可以看到,在自动化配置类SecurityAutoConfiguration中,最重要的就是导入了三个配置类,并且定义了一个默认的事件发布器。
导入的三个配置类中,SpringBootWebSecurityConfiguration的主要作用是在开发者没有提 供 WebSecurityConfigurerAdapter实例的情况下,由其负责提供一个默认的 WebSecurity
ConfigurerAdapter 实例,代码如下:
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(WebSecurityConfigurerAdapter.class) @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class) @ConditionalOnWebApplication(type = Type.SERVLET) public class SpringBootWebSecurityConfiguration { @Configuration(proxyBeanMethods = false) @Order(SecurityProperties.BASIC_AUTH_ORDER) static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter { } }
另一个导入的配置类 SecurityDataConfiguration 主要提供了 一个 SecurityEvaluationcontextExtension实例,以便通过SpEL为经过身份验证的用户提供数据查询:
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(SecurityEvaluationContextExtension.class) public class SecurityDataConfiguration { @Bean @ConditionalOnMissingBean public SecurityEvaluationContextExtension securityEvaluationContextExtension () { return new SecurityEvaluationContextExtension (); } }
最后一个导入的配置类WebSecurityEnablerConfigination则是我们分析的重点.
@Configuration(proxyBeanMethods = false) @ConditionalOnBean(WebSecurityConfigurerAdapter.class) @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @EnableWebSecurity public class WebSecurityEnablerConfiguration {}
WebSecurityEnablerConfiguration 配置类中添加了 @EnableWebSecurity 注解,而该注解的 定义,引入了关键的配置类WebSecurityConfiguration。
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { java.lang.annotation.ElementType.TYPE }) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class }) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { boolean debug () default false ; }
可以看到,@EnableWebSecurity是一个组合注解,首先导入了三个配置类:
WebSecurityConfiguration:用来配置 WebSecurity (重点分析)。 SpringWebMvcImportSelector:判断当前环境是否存在Spring MVC,如果存在,则引入相关配置。 OAuth2ImportSelector:判断当前环境是否存在OAuth2,如果存在,则引入相关配置 另外还有一个@EnableGlobalAuthentication注解,用来开启全局配置,代码如下:
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { java.lang.annotation.ElementType.TYPE }) @Documented @Import(AuthenticationConfiguration.class) @Configuration public @interface EnableGlobalAuthentication {}
可以看到,@EnableGlobalAuthentication注解的主要功能是导入了配置类AuthenticationConfiguration。
从上面的源码中我们可以看到,Spring Security的自动化配置类主要导入了两个类: WebSecurityConfiguration 和 AuthenticationConfiguration 接下来我们就来分析这两个类。
1.6.1 WebSecurityConfiguration WebSecurityConfiguration配置类的功能,主要就是为了构建Spring Security过滤器链代理对象FilterChainProxy。根据前面的分析,FiIterChainProxy是由Web Security来构建的,所以 在WebSecurityConfiguration中会首先构建WebSecurity对象,再利用WebSecurity对象构建岀 FilterChainProxy。
我们先来看一下WebSecurityConfiguration中定义的属性:
private WebSecurity webSecurity;private Boolean debugEnabled;private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;private ClassLoader beanClassLoader;@Autowired( required = false ) private ObjectPostProcessor<Object> objectObjectPostProcessor;
WebSecurityConfiguration 类实现了 ImportAware 接口。ImportAware 接口一般是和 @Import注解一起使用,实现了 ImportAware接口的配置类可以方便地通过setlmportMetadata 方法获取到导入类中的数据配置。换句话说,WebSecurityConfignration实现了 ImportAware接口,使用@Iniport 注解在@EnableWebSecurity 上导入 WebSecurityConfigination 之后,在 WebSecurityConfigination 的 setlmportMetadata 方法中可以方便的获取到@EnableWebSecurity 注解中的属性值,这里主要是debug属性。另一方面,WebSecuityConfiguration类通过实现 BeanClassLoaderAware 接口可以方便地获取到 ClassLoader对象。 webSecurity 对象是 WebSecurityConfiguration 中需要构建的 WebSecurity 对象。 webSecurityConfigurers 集合中保存了所有的配置类,也就是 WebSecurityConfignrerAdapter 对象,一个 WebSecurityConfigurerAdapter 对象可以创建一个 httpsSecurity,进而构建出一条过滤器链,多个WebSecurityConfigurerAdapter对象就可以构建出多条过滤器链。 beanClassLoader 是一个 ClassLoader。 objectObjectPostProcessor是一个对象后置处理器,注意这个对象是直接从Spring容器中注入的。下一小节会分析对象后置姓理器是什么时候初始化并注册到Sptmg容器中去的, 这是 WebSecurityConfiguration类中定义的属性。接下来,我们来看一下 setFilterChainProxySecurityConfigurer 方法,该方法主要用来构建一个 WebSecurity 对象,并且加载所有的配置类对象。
public void setFilterChainProxySecurityConfigurer (ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { this .webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity (objectPostProcessor)); if (this .debugEnabled != null ) { this .webSecurity.debug(this .debugEnabled); } webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE); Integer previousOrder = null ; Object previousConfig = null ; Iterator var5; SecurityConfigurer config; for (var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) { config = (SecurityConfigurer)var5.next(); Integer order = WebSecurityConfiguration.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; } var5 = webSecurityConfigurers.iterator(); while (var5.hasNext()) { config = (SecurityConfigurer)var5.next(); this .webSecurity.apply(config); } this .webSecurityConfigurers = webSecurityConfigurers; }
setFilterChainProxySecurityConfigurer 方法有两个参数,第一个参数 objectPostProcessor 是一个对象后置处理器,由于该方法有一个@Autowired注解,会自动查找需要注入的参数,所以objectPostProcessor参数会自动注入进来。需要注意的是,@Autowired注解的required属性为false,所以在方法参数注入的时候,有就注入,没有则忽略。required属性设置为false主要是针对第二个参数webSecurityConfigurers ,因为该参数的值是通过调用 autowiredWebSecurityConfigurersIgnoreParents 对象的 getWebSecurityConfigurers 方法获取的。 autowiredWebSecurityConfigurersIgnoreParents 对象也是在当前类中注入到 Spring 容器中的,我们来看一下它的 getWebSecurityConfigurers 方法:
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers () { List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList (); Map<String, WebSecurityConfigurer> beansOfType = this .beanFactory.getBeansOfType(WebSecurityConfigurer.class); Iterator var3 = beansOfType.entrySet().iterator(); while (var3.hasNext()) { Entry<String, WebSecurityConfigurer> entry = (Entry)var3.next(); webSecurityConfigurers.add(entry.getValue()); } return webSecurityConfigurers; }
可以看到,在 getWebSecurityConfigurers方法中主要是通过调用 beanFactory.getBeansOfType 方法来获取 Spring 容器中所有的 WebSecurityConfigurer 实例,也就是开发者自定义的各种各样继承自WebSecurityConfigurerAdapter的配置类。如果开发者没有自定义任何配置类,那么这里获取到的就是前面所讲的SprmgBootWebSecurityConfiguration 类中提供的默认配置类,将获取到的所有配置类实例放入webSecurityConfigurers集合中并返回。
返回setFilterChainProxySecurityConfigurer方法中,现在我们已经明白了第二个参数 webSecurityConfigruers的含义了,在该方法中,首先创建一个WebSecurity实例,创建出来之后去对象后置处理器中走一圈,这样就将webSecurity对象注册到Spring容器中了。接下来, 根据每一个配置类的@Order注解对webSecurityConfigurers集合中的所有配置类进行排序,因 为一个配置类对应一个过滤器链,当请求到来后,需要先和哪个过滤器链进行匹配,这里必然存在一个优先级问题,所以如果开发者自定义了多个配置类,则需要通过@Order注解标记 多个配置类的优先级。排序完成后,进入到for循环中,检查是否存在优先级相等的配置类, 如果存在,则直接抛出异常。最后再去遍历所有的配置类,调用webSecurity.apply方法将其添加到webSecurity父类中的configurers集合中(将来遍历该集合并分别调用配置类的init和configure方法完成配置类的初始化操作)。
这是setFiltetChainProxySecurityConfigurer方法的执行逻辑,该方法主要用来初始化 WebSecurity对象,同时收集到所有的自定义配置类。
有了 WebSecurity对象和配置类,接下来就可以构建过滤器FilterChainProxy 了。我们来看一下 springSecurityFilterChain 方法:
public Filter springSecurityFilterChain () throws Exception { boolean hasConfigurers = this .webSecurityConfigurers != null && !this .webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this .objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter () { }); this .webSecurity.apply(adapter); } return (Filter)this .webSecurity.build(); }
这里首先判断webSecurityConfigurers集合中是否存在配置类,如果不存在,则立马创建 一个匿名的 WebSecurityConfigurerAdapter对象并注册到Spring容器中,否则就直接调用 WebSecurity的build方法进行构建。
根据前面小节的介绍,了解了 WebSecurity对象的build方法执行后,首先会对所有的配置类即 WebSecurityConfigurerAdapter 实例进行构建,在 WebSecurityConfigurerAdapter 的 init 方法中,又会完成httpsSecurity的构建,而httpsSecurity的构建过程中,则会完成局部 AuthenticationManager对象以及每一个具体的过滤器的构建。
这就是整个过滤器链的构建流程。
1.6.2 Authenticationconfiguration 在Spring Security自动化配置类中导入的另外一个配置类是Authenticationconfiguration, 该类的功能主要是做全局的配置,同时提供-个全局的AuthenticationManager实例。首先我们来看 AuthenticationConfiguration 类的定义:
@Configuration( proxyBeanMethods = false ) @Import({ObjectPostProcessorConfiguration.class}) public class AuthenticationConfiguration
可以看到,AuthenticationConfiguration 类的定义中,导入了ObjectPostProcessorConfiguration配置,而ObjectPostProcessorConfiguration配置则提供了一个基本的对象后置处理器:
@Configuration( proxyBeanMethods = false ) @Role(2) public class ObjectPostProcessorConfiguration { public ObjectPostProcessorConfiguration () { } @Bean @Role(2) public ObjectPostProcessor<Object> objectPostProcessor (AutowireCapableBeanFactory beanFactory) { return new AutowireBeanFactoryObjectPostProcessor (beanFactory); } }
可以看到,ObjectPostProcessorConfiguration 类主要提供了一个 ObjectPostProcessor 实例, 具体的实现类是AutowireBeanFactoiyObjectPostProcessor,根据前面的介绍,该实现类主要用来将一个对象注册到Spring容器中去,我们在其他配置类中所见到的ObjectPostProcessor 实例其实都是这里提供的。
这是 AuthenticationConfiguration 类的定义部分,AuthenticationConfigiuation 类中的方法比较多,我们挑选出关键的部分分析一下:
查看代码
@Bean public AuthenticationManagerBuilder authenticationManagerBuilder (ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) { AuthenticationConfiguration.LazyPasswordEncoder defaultPasswordEncoder = new AuthenticationConfiguration .LazyPasswordEncoder(context); AuthenticationEventPublisher authenticationEventPublisher = (AuthenticationEventPublisher)getBeanOrNull(context, AuthenticationEventPublisher.class); AuthenticationConfiguration.DefaultPasswordEncoderAuthenticationManagerBuilder result = new AuthenticationConfiguration .DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder); if (authenticationEventPublisher != null ) { result.authenticationEventPublisher(authenticationEventPublisher); } return result; } @Bean public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer (ApplicationContext context) { return new AuthenticationConfiguration .EnableGlobalAuthenticationAutowiredConfigurer(context); } @Bean public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer (ApplicationContext context) { return new InitializeUserDetailsBeanManagerConfigurer (context); } @Bean public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer (ApplicationContext context) { return new InitializeAuthenticationProviderBeanManagerConfigurer (context); } public AuthenticationManager getAuthenticationManager () throws Exception { if (this .authenticationManagerInitialized) { return this .authenticationManager; } else { AuthenticationManagerBuilder authBuilder = (AuthenticationManagerBuilder)this .applicationContext.getBean(AuthenticationManagerBuilder.class); if (this .buildingAuthenticationManager.getAndSet(true )) { return new AuthenticationConfiguration .AuthenticationManagerDelegator(authBuilder); } else { Iterator var2 = this .globalAuthConfigurers.iterator(); while (var2.hasNext()) { GlobalAuthenticationConfigurerAdapter config = (GlobalAuthenticationConfigurerAdapter)var2.next(); authBuilder.apply(config); } this .authenticationManager = (AuthenticationManager)authBuilder.build(); if (this .authenticationManager == null ) { this .authenticationManager = this .getAuthenticationManagerBean(); } this .authenticationManagerInitialized = true ; return this .authenticationManager; } } }
首先定义了一个AuthenticationManagerBuilder实例,目的是为了构建全局的 AuthenticationManager 对象,这个过程中会从 Spring 容器中査找 AuthenticationEventPublisher 实例设置给 AuthenticationManagerBuilder 对象。
接下来构建了三个Bean,这三个Bean的作用在前面小节中已经介绍过了,这里就不再赘述了. getAuthenticationManager 方法则用来构建具体的 AuthenticationManager 对象,在该方法内部,会首先判断AuthenticationManager对象是否已经初始化,如果已经初始化,则直接 返回 AuthenticationManager 对象,否则就先从 Spring 容器中获取到 AuthenticationManagerBuilder对象。注意这里还多了一个AuthenticationManagerDelegator对象,这个主要是为了防止在初始化AuthenticationManager时进行无限递归,拿到authBuilder对象之后,接下来遍历 globalAuthConfigurers配置类集合(也就是第二点中所说的三个配置类),将配置类分别添加到authBuilder对象中,然后进行构建,最终将构建结果返回。 这是全局AuthenticationManager的构建过程。
整体来说,AuthenticationConfiguration的作用主要体现在两方面:第一就是导入了 ObjectPostProcessorConfiguration 配置类;第二则是提供 了一个全局的 AuthenticationManager 对象。
如果升发者在自定义配置类中重写了 configure(AuthenticationManagerBuilder)方法,这里的全局AuthenticationManager对象将不会生效,而大部分情况下,开发者都会重写 configure(AuthenticationManagerBuilder)方法。
至此,Spring Security初始化就讲解完了。然而这里的架构复杂,概念繁多,可能有读者看完之后还是理解不到位,因此,接下来我们将通过几个不同的案例,展示前面这些组件的不同用法,加深大家对Spring Security基础组件的理解。