在上一篇中我们梳理了spring security 是怎样一步步调用到过滤器链的,下面我们来看下框架是怎么构造过滤器链的,以及过滤器链中主要的过滤器
1.过滤器链的构造
1.1自动配置
springboot 从2.7开始需要被自动加载的类要写在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里,每一行为配置类的全类名,为了向下兼容目前springboot还支持原来的spring.factories配置,但是打开spring.factories文件看里面的配置很少了已经大部分被移到了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。
在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中我们可以看到如下和spring security相关自动配置:
其中有一条配置为org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,我们看下SecurityAutoConfiguration的源码如下:
在类中通过使用@EnableConfigurationProperties注解读取配置,并使用@Import导入其他配置
@AutoConfiguration @ConditionalOnClass({DefaultAuthenticationEventPublisher.class}) @EnableConfigurationProperties({SecurityProperties.class}) @Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class}) public class SecurityAutoConfiguration { public SecurityAutoConfiguration() { } @Bean @ConditionalOnMissingBean({AuthenticationEventPublisher.class}) public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) { return new DefaultAuthenticationEventPublisher(publisher); } }
进入导入的SpringBootWebSecurityConfiguration中源码(因为源码太长,有减缩)如下:
可见在SpringBootWebSecurityConfiguration类中使用了@EnableWebSecurity注解,该注解为springsecurity的启用注解,这就是为啥我们只需要引入pom依赖,不需要任何配置就会自动启用springsecurity的原因。
@Configuration( proxyBeanMethods = false ) @ConditionalOnWebApplication( type = Type.SERVLET ) class SpringBootWebSecurityConfiguration { SpringBootWebSecurityConfiguration() { } @Configuration( proxyBeanMethods = false ) @ConditionalOnMissingBean( name = {"springSecurityFilterChain"} ) @ConditionalOnClass({EnableWebSecurity.class}) @EnableWebSecurity static class WebSecurityEnablerConfiguration { WebSecurityEnablerConfiguration() { } } ....
进入EnableWebSecurity 源码如下,可见在类中同样使用了@Import注解来导入其他配置
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class}) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { boolean debug() default false; }
进入 源码 我们可以看到注入springSecurityFilterChain
@Configuration( proxyBeanMethods = false ) public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware { private WebSecurity webSecurity; private Boolean debugEnabled; private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers; private List<SecurityFilterChain> securityFilterChains = Collections.emptyList(); private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList(); ...... @Bean( name = {"springSecurityFilterChain"} ) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty(); boolean hasFilterChain = !this.securityFilterChains.isEmpty(); Assert.state(!hasConfigurers || !hasFilterChain, "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one."); if (!hasConfigurers && !hasFilterChain) { WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() { }); this.webSecurity.apply(adapter); } Iterator var7 = this.securityFilterChains.iterator(); while(true) { while(var7.hasNext()) { SecurityFilterChain securityFilterChain = (SecurityFilterChain)var7.next(); this.webSecurity.addSecurityFilterChainBuilder(() -> { return securityFilterChain; }); Iterator var5 = securityFilterChain.getFilters().iterator(); while(var5.hasNext()) { Filter filter = (Filter)var5.next(); if (filter instanceof FilterSecurityInterceptor) { this.webSecurity.securityInterceptor((FilterSecurityInterceptor)filter); break; } } } var7 = this.webSecurityCustomizers.iterator(); while(var7.hasNext()) { WebSecurityCustomizer customizer = (WebSecurityCustomizer)var7.next(); customizer.customize(this.webSecurity); } return (Filter)this.webSecurity.build(); } } ......
下面我们看下springSecurityFilterChain怎么构建的,进入最后一行(Filter)this.webSecurity.build()方法,源码如下:
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> { private AtomicBoolean building = new AtomicBoolean(); private O object; public AbstractSecurityBuilder() { } public final O build() throws Exception { if (this.building.compareAndSet(false, true)) { this.object = this.doBuild(); return this.object; } else { 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"); } else { return this.object; } } protected abstract O doBuild() throws Exception; }
我们看出在build方法中调用了doBuild()抽象方法,我们再看下这个方法的 实现
protected final O doBuild() throws Exception { synchronized(this.configurers) { this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING; this.beforeInit(); this.init(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING; this.beforeConfigure(); this.configure(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING; O result = this.performBuild(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT; return result; } }
在doBuild的实现方法中,我们看到了调用this.performBuild()方法下面我们进入this.performBuild()源码
protected Filter performBuild() throws Exception { Assert.state(!this.securityFilterChainBuilders.isEmpty(), () -> { return "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this is done by exposing a SecurityFilterChain bean. More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly"; }); int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize); List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList(); Iterator var4 = this.ignoredRequests.iterator(); while(var4.hasNext()) { RequestMatcher ignoredRequest = (RequestMatcher)var4.next(); this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest + ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead."); SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]); securityFilterChains.add(securityFilterChain); requestMatcherPrivilegeEvaluatorsEntries.add(this.getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain)); } var4 = this.securityFilterChainBuilders.iterator(); while(var4.hasNext()) { SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var4.next(); //为每个httpSecurity生成一条过滤器链 SecurityFilterChain securityFilterChain = (SecurityFilterChain)securityFilterChainBuilder.build(); securityFilterChains.add(securityFilterChain); requestMatcherPrivilegeEvaluatorsEntries.add(this.getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain)); } if (this.privilegeEvaluator == null) { this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(requestMatcherPrivilegeEvaluatorsEntries); } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (this.httpFirewall != null) { filterChainProxy.setFirewall(this.httpFirewall); } if (this.requestRejectedHandler != null) { filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; if (this.debugEnabled) { this.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); } this.postBuildAction.run(); return (Filter)result; }