基本原理
spring security通过一系列filter来对请求进行拦截
网上一个比较好的图
由于是一个filterChain,因此如果我在FilterSecrutiyInterceptor,即最后一个过滤器上打断点,一定能够通过debug的方式来得到整个过滤器链条
在最后一个filter.doFilter方法上打一个断点,
得到整个filterChain,进一步验证了上面图的正确性。
但是这些filter是什么时候添加进去的呢?从security-start的自动配置类开始分析
security-start 自动配置原理
1. SecurityAutoConfiguration类中导入了SpringBootWebSecurityConfiguration
@Import({ SpringBootWebSecurityConfiguration.class,
AuthenticationManagerConfiguration.class,
BootGlobalAuthenticationConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
2.SpringBootWebSecurityConfiguration中加了@EnableWebSecurity
@EnableWebSecurity
public class SpringBootWebSecurityConfiguration {
3.EnableWebSecurity注解中导入了WebSecurityConfiguration类
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
FilterChain的构造过程
1. WebSecurityConfiguration
这是配置类的入口类
1.1 setFilterChainProxySecurityConfigurer
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = 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;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
这个类的主要作用
1.1.1 构建WebSecurity对象
1.1.2 获得security相关的配置
- 通过这个调用BeanFactory中Bean的名字为autowiredWebSecurityConfigurersIgnoreParents的getWebSecurityConfigurers方法得到webSecurityConfigurers,并把这些配置加入到webSecurity对象中
通过debug可以看到有4个,
1. 自己定义的安全相关配置类
BrowserSecurityConfig
- security starter autoconfigure 自动注入的
SpringBootWebSecurityConfiguration$IgnoredPathsWebSecurityConfigurerAdapter
SpringBootWebSecurityConfiguration$ApplicationWebSecurityConfigurerAdapter- 用于管理的,暂时没看到在哪个地方注入的
ManagementWebSecurityAutoConfiguration$ManagementWebSecurityConfigurerAdapter
1.2 调用springSecurityFilterChain方法来生成FiterChain
这个方法是个模板方法,定义了整个生成的骨架
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
//1.init,调用上面每个webSecurityConfigurers的init
init();
buildState = BuildState.CONFIGURING;
//2. 设置AuthenticationManager
beforeConfigure();
//3. 调用上面每个webSecurityConfigurers的configure方法
configure();
buildState = BuildState.BUILDING;
//4.生成filterChain
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
- WebSecurity UML类图
webSecurity.build()
应用的设计思想分析:
- 模板方法模式:
公共的配置如init(),configure()抽象到父类,
把需要具体对象实现的放到了子类实现.
如beforeInit(),beforeConfigure();performBuild做为抽象方法,需要具体的对象去实现. - 泛型与策略模式的灵活运用
虽然init(),confiure()是公共的方法,但是针对不同类型的操作是不一样的,
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
这样定义传入一个this,如果是http,就会传入httpsecurity的参数,如果是web就传入websecurity类型的参数,有点类似于策略模式
如何生成具体的filterChain,当然这是调用的是WebSecurity的方法,
protected Filter performBuild() throws Exception {
...
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
chainSize);
//1.首先添加不需要认证的url过滤器,这里面的url是静态资源文件目录
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
//2.添加需要经过安全认证的过滤器
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
//3.生成过滤器的代理
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
//4.真正的build方法执行
postBuildAction.run();
return result;
}
上面的关键点是securityFilterChainBuilder.build()这个方法,securityFilterChainBuilder是SecurityBuilder类型的实例,从上面的类图来看,httpsecurity也是这个接口的实例,实际上这个就是init()方法中创建的httpsecurity对象
上面的分析是大概的整体flow,现拿一个做为例子:
BrowserSecurityConfig如何生成过滤器链
- WebSecurity.build()->AbstractConfiguredSecurityBuilder#doBuild.build
BrowserSecurityConfig.init->beforeConfigure->configure->performBuild->postBuildAction.run();
1.init
这里使用的是模板方法设计模式
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
gethttp()->to create the http object and set the default param
@SuppressWarnings({ "rawtypes", "unchecked" })
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
//设置后置处理器?
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//获得AuthenticationManager
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
//添加一系列filter的默认配置
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
//增加用户自定义的配置,重写这个方法,就会对上面的security chain进行添加或修改
configure(http);
return http;
}
关于泛型的声明
//声明
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
//实例
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<AnonymousConfigurer<H>, H> {
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
extends AbstractHttpConfigurer<T, B> {
performBuild
protected Filter performBuild() throws Exception {
...
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
chainSize);
//1.首先添加不需要认证的url过滤器,这里面的url是静态资源文件目录
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
//2.添加需要经过安全认证的过滤器
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
//3.生成过滤器的代理
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
//4.真正的build方法执行
postBuildAction.run();
return result;
}
需要拿 到所有的securityFilterChainBuilder,即init中创建的httpSecurity对象,然后进行生成
可以看到这里有三个securityFilterChainBuilder,每个securityFilterChainBuilder里面包含configures,sharedObjects等对象
securityFilterChainBuilder.build就是根据这些配置文件来生成filter的,再次调用AbstractConfiguredSecurityBuilder#doBuild如果
整理
- WebSecurityConfiguration#springSecurityFilterChain
- WebSecurity#build()
- AbstractSecurityBuilder#build()->dobuild()
- AbstractConfiguredSecurityBuilder#doBuild
-
- init()
遍历所有的configurer,调用init方法
- init()
-
- beforeConfigure()
抽象方法,调用WebSecurity.beforeConfirue()
- beforeConfigure()
-
- configure()
遍历所有的configure,调用configure()方法
- configure()
-
- performBuild
抽象方法,调用WebSecurity.performBuild()
- performBuild
-
- AbstractConfiguredSecurityBuilder#doBuild
- AbstractSecurityBuilder#build()->dobuild()
- WebSecurity#build()
上面的流程,第2步与第4步调用WebSecurity这个类的方法,因此最大的变化在第1,3步,里面
BrowserSecurityConfig
//security starter autoconfigure 自动注入的
SpringBootWebSecurityConfiguration
ApplicationWebSecurityConfigurerAdapter
//用于管理的,暂时没看到在哪个地方注入的
ManagementWebSecurityAutoConfiguration
IgnoredPathsWebSecurityConfigurerAdapter
SpringBootWebSecurityConfiguration
ManagementWebSecurityConfigurerAdapter
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
这里最主要的是getHttp这个方法,查看这个方法,发现构建了http请求,并且最后也调用了configure(http)来配置http
也就是说如果我们自定义的类重写了这个方法,那么就用的是我们自定义的。
Q:如果这里也调用了configure方法,那么上面那第三步的config方法是不是就没有必要调用了?是我自己理解有误吗?
A:的确是理解有误,所有的构建在init中即第1步已经完成了,第3步中的configure,
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
可以看到这里参数是泛型B,传递的是this对象,由开始知道,我们调用的是WebSecurity,而不是HttpSecurity对象,
因此这里调用 的下面这个方法,其实是个空方法(以前以为configure(HttpSecurity http)),
public void configure(WebSecurity web) throws Exception {
}
Q:按这个逻辑,最后执行performBuild,来构建整个filter链,没啥问题,
问题是这个方法中会调用
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
再次掉入了这个创建的循环中
A: AbstractConfiguredSecurityBuilder#configure
并未进入循环,这里的getConfigurres为null
最后一步的performBuild,调用的也是authenticationManagerBuilder#performBuild,并不是前面的performBuild
如果当前对象是httpSecurity,则调用所有configure方法,生成对应的filterchain
Q:哪里生成SecurityBuilder?
A:WebSecurityConfigurerAdapter#init方法中添加了,这里即HttpSecurity这个对象
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}