Spring Security解析三:SecurityFilterChain创建过程章节说到,WebSecurityConfigurerAdapter是构建SecurityFilterChain的关键,在WebSecurityConfigurerAdapter的init方法中会创建一个SecurityBuilder<SecurityFilterChain>类型的实例对象【HttpSecurity】并保存到WebSecurity的securityFilterChainBuilders属性中,后续通过SecurityBuilder来完成SecurityFilterChain的创建。
WebSecurityConfigurerAdapter的源码如下
/**
* 在WebSecurity中会分别执行其init、configure方法。
* 在init中创建的HttpSecurity 在WebSecurity会调用其build方法
* 来得到SecurityFilterChain(里面有一个判断
* 是否匹配某个请求的matches方法,和一个返回多个Filter集合的getFilters方法,
* 这些Filter就是拦截后需要被逐个执行的,所以这些Filter的创建才是关键)
*/
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
private AuthenticationConfiguration authenticationConfiguration;
private AuthenticationManagerBuilder authenticationBuilder;
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
private boolean disableLocalConfigureAuthenticationBldr;
private boolean authenticationManagerInitialized;
private AuthenticationManager authenticationManager;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private HttpSecurity http;
private boolean disableDefaults;
// AuthenticationConfiguration为认证配置,
//在@EnableWebSecurity注解里面的@EnableGlobalAuthentication中
@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
//创建用于构建AuthenticationManager的对象
//与@AuthenticationConfiguration中十分类似
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
//创建可用于在这里进行配置的AuthenticationManagerBuilder
//该DefaultPasswordEncoderAuthenticationManagerBuilder在
//@AuthenticationConfiguration中也使用到了
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
//设置“是否删除凭据”
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
};
}
/**
* Gets the {@link AuthenticationManager} to use. The default strategy is if
* {@link #configure(AuthenticationManagerBuilder)} method is overridden to use the
* {@link AuthenticationManagerBuilder} that was passed in. Otherwise, autowire the
* {@link AuthenticationManager} by type.
*
* @return the {@link AuthenticationManager} to use
* @throws Exception
*/
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
//调用下面的方法进行装配(默认是设置disableLocalConfigureAuthenticationBldr为true)
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
//不使用本地的配置(则会使用@AuthenticationConfiguration中的配置)
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
//使用本地的装配
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
/*
* 可以通过重写该方法来配置AuthenticationManagerBuilder
* 该方法被重写后,@AuthenticationConfiguration中配置
* 的AuthenticationManager将变得无用
*(前提是disableLocalConfigureAuthenticationBldr不能被设置为true)
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
//创建的实例被添加到sharedObjects中
protected UserDetailsService userDetailsService() {
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(
localConfigureAuthenticationBldr, globalAuthBuilder));
}
public void init(final WebSecurity web) throws Exception {
//创建HttpSecurity的实例(该对象后面会详细探讨)
final HttpSecurity http = getHttp();
//调用WebSecurity的方法将http传递给它保存,
//后面WebSecurity会使用http的build()方法来构建Filter,
//所有真正构建Filter的是这个HttpSecurity 。
//同时,会通过Lambda表达式创建一个Runnable类型的实例
//传递给WebSecurity,待创建好Filter实例后,最后回调该方法
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
//供WebSecurity回调的方法
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
//这里只是简单的给WebSecurity的filterSecurityInterceptor属性赋值
//在WebSecurity的getPrivilegeEvaluator方法中会用到
web.securityInterceptor(securityInterceptor);
});
}
//该方法默认什么都没做
public void configure(WebSecurity web) throws Exception {
}
//关键方法
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
/*
*创建AuthenticationManager实例,可能来源于本类中的配置,
*也可能来源于其它地方,例如@AuthenticationConfiguration
*/
AuthenticationManager authenticationManager = authenticationManager();
/*
*设置父级AuthenticationManager。当其他的AuthenticationManager不能
*进行身份认证时,最后使用该AuthenticationManager进行认证。
*
* 可见,默认的AuthenticationManager被设置为了父级AuthenticationManager,
*也就是说只会在我们AuthenticationManager不能进行验证的情况下才会使用这个默认的
*/
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
//这这里配置了许多默认的过滤器
http
.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:on
ClassLoader classLoader = this.context.getClassLoader();
/*
* 从IOC中得到所有AbstractHttpConfigurer类型的Bean,并作为默认的配置加入
* 所以默认情况下(disableDefaults为false),我们只需要在Bean上下文中
* 创建AbstractHttpConfigurer类型的Bean也可以添加进配置中
*/
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
//配置默认的行为
configure(http);
return http;
}
/*
*添加默认的配置;这里的authorizeRequests()方法的调用会配置
*ExpressionUrlAuthorizationConfigurer对象,而该对象会在拦截链的
*末尾添加FilterSecurityInterceptor拦截器
*/
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests() // ExpressionUrlAuthorizationConfigurer
.anyRequest().authenticated() //匹配任何请求、ExpressionUrlAuthorizationConfigurer
.and()
.formLogin().and() //FormLoginConfigurer
.httpBasic(); //HttpBasicConfigurer
}
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.put(UserDetailsService.class, userDetailsService());
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
return sharedObjects;
}
@Autowired(required = false)
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
this.trustResolver = trustResolver;
}
@Autowired(required = false)
public void setContentNegotationStrategy(
ContentNegotiationStrategy contentNegotiationStrategy) {
this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
}
HttpSecurity的继承关系与WebSecurity几乎一样,只是多实现了一个HttpSecurityBuilder<HttpSecurity>接口,同时声明的泛型不一样。当在WebSecurity中调用HttpSecurity的build()方法构建DefaultSecurityFilterChain时,依然会逐个的执行HttpSecurity的beforeInit()、init()、beforeConfigure()、configure()以及performBuild()方法来完成构建。
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
private final RequestMatcherConfigurer requestMatcherConfigurer;
//保存添加的Filter,拦截请求后要执行的过滤器集合
private List<Filter> filters = new ArrayList<>();
//用于匹配请求是否满足执行这些过滤器的条件
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
private FilterComparator comparator = new FilterComparator();
/*
* @param authenticationBuilder 该对象里面保存了AuthenticationManager、
* UserDetailsService、AuthenticationProvider对象等
*
*/
public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,
AuthenticationManagerBuilder authenticationBuilder,
Map<Class<?>, Object> sharedObjects) {
super(objectPostProcessor);
Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
//保存到变量中
setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
//遍历传递进来的对象并添加都自身的属性变量中
for (Map.Entry<Class<?>, Object> entry : sharedObjects
.entrySet()) {
setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
}
//获取上下文对象
ApplicationContext context = (ApplicationContext) sharedObjects
.get(ApplicationContext.class);
this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
}
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() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
//添加配置
return apply(configurer);
}
public HttpSecurity authenticationProvider(
AuthenticationProvider authenticationProvider) {
getAuthenticationRegistry().authenticationProvider(authenticationProvider);
return this;
}
private AuthenticationManagerBuilder getAuthenticationRegistry() {
return getSharedObject(AuthenticationManagerBuilder.class);
}
public HttpSecurity 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;
}
///////////////////////////////以下为配置调用方法/////////////////////////
//添加OpenIDLoginConfigurer配置
public OpenIDLoginConfigurer<HttpSecurity> openidLogin() throws Exception {
return getOrApply(new OpenIDLoginConfigurer<>());
}
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
return getOrApply(new HeadersConfigurer<>());
}
public HttpSecurity headers(Customizer<HeadersConfigurer<HttpSecurity>> headersCustomizer) throws Exception {
headersCustomizer.customize(getOrApply(new HeadersConfigurer<>()));
return HttpSecurity.this;
}
public CorsConfigurer<HttpSecurity> cors() throws Exception {
return getOrApply(new CorsConfigurer<>());
}
public HttpSecurity cors(Customizer<CorsConfigurer<HttpSecurity>> corsCustomizer) throws Exception {
corsCustomizer.customize(getOrApply(new CorsConfigurer<>()));
return HttpSecurity.this;
}
public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
return getOrApply(new SessionManagementConfigurer<>());
}
public HttpSecurity sessionManagement(Customizer<SessionManagementConfigurer<HttpSecurity>> sessionManagementCustomizer) throws Exception {
sessionManagementCustomizer.customize(getOrApply(new SessionManagementConfigurer<>()));
return HttpSecurity.this;
}
public PortMapperConfigurer<HttpSecurity> portMapper() throws Exception {
return getOrApply(new PortMapperConfigurer<>());
}
public HttpSecurity portMapper(Customizer<PortMapperConfigurer<HttpSecurity>> portMapperCustomizer) throws Exception {
portMapperCustomizer.customize(getOrApply(new PortMapperConfigurer<>()));
return HttpSecurity.this;
}
public X509Configurer<HttpSecurity> x509() throws Exception {
return getOrApply(new X509Configurer<>());
}
public HttpSecurity x509(Customizer<X509Configurer<HttpSecurity>> x509Customizer) throws Exception {
x509Customizer.customize(getOrApply(new X509Configurer<>()));
return HttpSecurity.this;
}
public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
return getOrApply(new RememberMeConfigurer<>());
}
public HttpSecurity rememberMe(Customizer<RememberMeConfigurer<HttpSecurity>> rememberMeCustomizer) throws Exception {
rememberMeCustomizer.customize(getOrApply(new RememberMeConfigurer<>()));
return HttpSecurity.this;
}
... ...
}
总的来说,在 WebSecurityConfigurerAdapter 中创建了HttpSecurity 实例,并将该实例存入WebSecurity的集合中,同时在WebSecurityConfigurerAdapter中为HttpSecurity 配置了一些默认的行为,例如CsrfConfigurer,ExpressionUrlAuthorizationConfigurer、FormLoginConfigurer等。到目前为止,从SpringBoot的启动到Security的Filter的创建和注册过程都已完成,接下来的问题是认证和授权又是在哪里进行的呢? 关于这方面的问题留在后面的章节中来探讨。
再来整体回顾下初始化的过程
在配置类上添加@EnableWebSecurity来启动装配(SpringBoot中该步骤会自动完成);
-
EnableWebSecurity中导入了配置类WebSecurityConfiguration并执行:
- 从IOC中收集到所有SecurityConfigurer<Filter, WebSecurity>类型的Bean并根据Order注解的值进行排序(默认得到的个数为0):
- 创建WebSecurity实例对象;
- 将得到的所有SecurityConfigurer<Filter, WebSecurity>类型Bean通过webSecurity的apply方法添加到集合中;
- 开始初始化定义的名为springSecurityFilterChain的Filter类型的Bean(FilterChainProxy):
在初始化的过程中,首先判断收集的SecurityConfigurer<Filter, WebSecurity>类型的Bean的个数是否为0,如果为0则使用默认的WebSecurityConfigurerAdapter作为安全的配置,并也通过apply方法添加到WebSecurity的集合中,接着执行WebSecurity的build()方法完成创建;WebSecurityConfigurerAdapter中创建了一个SecurityConfigurer类型的HttpSecurity实例来真正完成安全的装配,HttpSecurity也提供了很多方法来方便对各种安全机制的配置;
WebSecurity的build()方法通过调用doBuild()方法来执行各个装配的过程方法:beforeInit()、init()、beforeConfigure()、configure()与performBuild();这几个方法的调用过程也就是执行WebSecurity中各个SecurityConfigurer的这几个方法的过程;
-
WebSecurity的performBuild()方法执行后便返回了FilterChainProxy实例,该实例里面保存了每个SecurityConfigurer所生成的SecurityFilterChain(也就是说,一个SecurityConfigurer装配执行完后会得到一个SecurityFilterChain对象);SecurityFilterChain(DefaultSecurityFilterChain)实例里面保存了SecurityConfigurer中装配的各个Filter对象(List<Filter>)以及进行路径匹配验证的RequestMatcher实例对象。
安全配置和验证过程本质就是Filter的创建以及对请求拦截的处理过程
最后在spring-boot-autoconfigure/META-INF/spring.factories里面定义的启动类SecurityFilterAutoConfiguration中创建DelegatingFilterProxyRegistrationBean委托注册实例,该实例委托的正是上面创建的名为springSecurityFilterChain的Filter,也就是FilterChainProxy。通过该委托将springSecurityFilterChain在Servlet容器启动的时候添加到Filter链中。