前言
接着上一篇,我们来谈谈WebSecurity的规则是如何从我们配置规则加入到整个SpringSecurity的认证链条的。
配置
回顾一下上一篇那个简单的WebSecurity配置
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder.encode("123456"))
.authorities("admin");
// @formatter:on
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/favicon.ico");
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
配置方法
上面的三个config配置方法里,应用了许多配置项。
比如httpBasic()方法,从下面的源代码可以看出,它会实例化HttpBasicConfigurer类,并应用到HttpSecurity里。
public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
return getOrApply(new HttpBasicConfigurer<>());
}
又比如inMemoryAuthentication()方法。
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return apply(new InMemoryUserDetailsManagerConfigurer<>());
}
无论是HttpBasicConfigurer还是InMemoryUserDetailsManagerConfigurer,都有同一个父类SecurityBuilder。
builder设计模式
SecurityBuilder类是一个建造者类,只有一个build方法。
public interface SecurityBuilder<O> {
O build() throws Exception;
}
SecurityBuilder有众多继承类,下面展示了其中的一部分。
还记得上一篇setFilterChainProxySecurityConfigurer方法创建的webSeurity对象,它也继承SecurityBuilder类。当webSeurity.build()后,就会引发它下面所有的SecurityBuild继承类的调用build方法,如上面说到的HttpBasicConfigurer和InMemoryUserDetailsManagerConfigurer类。
诞生
开始进入主题,看看webSecurity.build()是怎么一步步应用我们的配置三个方法的。
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
1. 三个自定义配置
1.1 configure(AuthenticationManagerBuilder auth)
先看看是如何一步步来到的configure(AuthenticationManagerBuilder auth)方法。
WebSecurity.build() ->
AbstractSecurityBuilder.build() ->
AbstractConfiguredSecurityBuilder.doBuild() ->
AbstractConfiguredSecurityBuilder.init()->
WebSecurityConfigurerAdapter.init(final WebSecurity web)->
WebSecurityConfigurerAdapter.getHttp()->
WebSecurityConfigurerAdapter.authenticationManager()->
自定义WebSecurityConfig->configure(AuthenticationManagerBuilder auth)
1.2 configure(HttpSecurity http)
然后是来到的configure(HttpSecurity http)方法。
WebSecurity.build() ->
AbstractSecurityBuilder.build() ->
AbstractConfiguredSecurityBuilder.doBuild() ->
AbstractConfiguredSecurityBuilder.init()->
WebSecurityConfigurerAdapter.init(final WebSecurity web)->
WebSecurityConfigurerAdapter.getHttp()->
自定义WebSecurityConfig->configure(HttpSecurity http)
1.3 configure(WebSecurity web)
最后是来到的configure(WebSecurity http)方法。
WebSecurity.build() ->
AbstractSecurityBuilder.build() ->
AbstractConfiguredSecurityBuilder.doBuild() ->
AbstractConfiguredSecurityBuilder.configure()->
自定义WebSecurityConfig->configure(WebSecurity web)
FilterChainProxy
在springSecurityFilterChain Bean的构建中,会调用下面的performBuild()方法。于是就创建了FilterChainProxy实例,并会添加我们自定义配置到securityFilterChains中。ignoredRequests就是我们配置configure(WebSecurity web)方法中的web.ignoring().antMatchers("/css/", "/js/", “/favicon.ico”);而securityFilterChainBuilders就是我们配置的configure(HttpSecurity http)。
protected Filter performBuild() throws Exception {
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);
Filter result = filterChainProxy;
postBuildAction.run();
return result;
}
2. ignoredRequest
下图可以清楚的看到3个ignoredRequest
3. securityFilterChain
securityFilterChain情况就比较复杂了,会合并我们自定义配置和默认配置
3.1默认配置
在WebSecurity.build()方法被调用时,还有一段SpringSecurity设置默认配置的代码,如下
protected final HttpSecurity getHttp() throws Exception {
...
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and() // 1
.addFilter(new WebAsyncManagerIntegrationFilter()) // 2
.exceptionHandling().and() // 3
.headers().and() // 4
.sessionManagement().and() // 5
.securityContext().and() // 6
.requestCache().and() // 7
.anonymous().and() // 8
.servletApi().and() // 9
.apply(new DefaultLoginPageConfigurer<>()).and() //10
.logout(); //11
// @formatter:on
...
}
configure(http);
return http;
}
从上面SpringSecurity提供的默认配置可以看出,SpringSecurity默认地为我们添加了11个SecurityConfigurer和Filter。
3.2 SecurityConfigurer和Filter
以SpringSecurity提供的默认配置,csrf()方法为例。
首先,csrf()方法应用了一个CsrfConfigurer配置类,这个类继承自SecurityConfigurer
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
...
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<>(context));
}
...
}
接着,CsrfConfigurer实现了configure(H http)方法,此方法会实例化一个CsrfFilter。
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
...
@Override
public void configure(H http) throws Exception {
CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
if (requireCsrfProtectionMatcher != null) {
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
}
AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
if (accessDeniedHandler != null) {
filter.setAccessDeniedHandler(accessDeniedHandler);
}
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
logoutConfigurer
.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
}
SessionManagementConfigurer<H> sessionConfigurer = http
.getConfigurer(SessionManagementConfigurer.class);
if (sessionConfigurer != null) {
sessionConfigurer.addSessionAuthenticationStrategy(
new CsrfAuthenticationStrategy(this.csrfTokenRepository));
}
filter = postProcess(filter);
http.addFilter(filter);
}
...
}
最终,这个Filter会被添加到到http,而这个http就是HttpSecurity类,是本篇在开头提到的getHttp()方法中实例出来的,同时也是我们的自定义配置类中可以看到的那个HttpSecurity http。
4. 最终产物
上篇提到的springSecurityFilterChain是一个Bean。如果按照我们自定义的配置,它会包括4个DefaultSecurityFilterChain。其中三个filterChain是我们配置的web.ignoring().antMatchers("/css/", "/js/", “/favicon.ico”),另外一个是我们配置的configure(HttpSecurity http)。HttpSecurity因为保留了SpringSecurity的默认配置,所以会有我们配置之外的默认配置。
从上图可以看出,每一个HttpRequest都会经过4个FilterChain。
总结
本篇介绍了springSecurityFilterChain的形成,它最终包含了4个filterChain。其中HttpSecurity生成的filterChainer,包含了多个filter。那么下篇再谈谈这些Filter是怎么帮助我们来拦截或者处理HttpRequest的。