我们知道, Spring Security配置成功后, 可以拦截制定的http请求来进行检证和授权.
拦截http请求, 通常都是Filter做的事情, 那Spring Security的Filter是哪个类, 又是怎么生效的呢?
@EnableWebSecurity简析
@EnableWebSecurity是开启Spring Security的注解. 主要代码有:
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
首先分析:WebSecurityConfiguration
有一个创建Filter的方法:
@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();
}
这个Bean的name是: springSecurityFilterChain
这里引起了2个问题:
- 一般的Filter需要通过Servlet的标准注解, 才能让Servlet容器识别并生效, 并且还要配置匹配的url等信息.通过Spring 搞出的这个Filter只是一个受Spring 容器管理的一个Bean而已, 并不能被Servlet容器识别, 响应http请求.
- 这个Filter的真正实现类是什么? webSecurity.build()只是一个构建者模式的表达.
我们先分析问题2.
build()方法的定义实际上是在AbstractSecurityBuilder中定义:
真正的逻辑委托给抽象方法doBuild实现, 显然doBuild需要子类去实现:
也就是在AbstractConfiguredSecurityBuilder(WebSecurity的父类)中实现的:
可见核心是委托给了抽象方法:performBuild, 这个performBuild的实现是在WebSecurity中, 所以现在把焦点移回到:WebSecurity的performBuild
这里终于知道这个Filter的真正身份了:
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
就是这个:FilterChainProxy
看看这个类的构造子:
里面包含了一个SecurityFilterChain的集合.
再看下SecurityFilterChain
里面包含了一组真正的Filter.
下面我们分析下这个: securityFilterChains
我们再看下performBuild()的前面几行代码:
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);
可以看到这个securityFilterChains的集合的值来源于2部分:ignoredRequests和securityFilterChainBuilders
ignoredRequests就是我们不想应用过滤器的url的集合, 就是通常配置在Spring Security的配置类中的, 比如有个自定义的配置类:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean {
}
里面关于有些不要经过Filter chain的url的定义方式如下:
@Override
public void configure(WebSecurity web) throws Exception {
List<String> antUrlList = authProperties().getAntUrl();
antUrlList.add("/404");
antUrlList.add("/403");
antUrlList.add("/500");
antUrlList.add("/error");
String[] antUrls = authProperties().getAntUrl().toArray(new String[antUrlList.size()]);
web.ignoring().antMatchers(antUrls);
super.configure(web);
}
下面主要看看:securityFilterChainBuilders是什么
默认情况下securityFilterChainBuilders里面只有一个对象,那就是HttpSecurity
具体原理, 我们后面分析.
到这里, 我们知道了FilterChainProxy这个Filter 表示的就是:springSecurityFilterChain
那它是怎么起作用的呢, 这就引入了:DelegatingFilterProxy
AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer接口. 这个接口会被Servlet识别并调用onStartup()方法.
也就是说, DelegatingFilterProxy可以被Servlet容器识别.
在AbstractSecurityWebApplicationInitializer的有:
可见:在onStartup()方法里调用了:insertSpringSecurityFilterChain, 这里创建了
DelegatingFilterProxy, 它是一个真正的filter
默认拦截所有的请求. 拦下来后做什么呢?
我们再看DelegatingFilterProxy
其父类GenericFilterBean继承了Filter, 所以本质上DelegatingFilterProxy也是一个Filter, 它的doFilter方法:
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
也就是说delegate才是真正的doFilter的宿主.
那:delegate是什么呢?
那doFilter()之前会执行init()方法, 看代码发现最终会执行到:
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
这里最关键的就变成了:targetBeanName
再看看DelegatingFilterProxy的构造子:
public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
this.setTargetBeanName(targetBeanName);
this.webApplicationContext = wac;
if (wac != null) {
this.setEnvironment(wac.getEnvironment());
}
}
那这个构造函数又是怎么调用的呢?
让我们接着抽丝剥茧, 离真相已经非常接近了!
利用IDE的find usage功能, 查看这个构造谁调用了:
只有一处类的调用,没错,就是它:
DelegatingFilterProxyRegistrationBean
@Override
public DelegatingFilterProxy getFilter() {
return new DelegatingFilterProxy(this.targetBeanName,
getWebApplicationContext()) {
@Override
protected void initFilterBean() throws ServletException {
// Don't initialize filter bean on init()
}
};
}
我cha, 这个吊玩意, 又是:this.targetBeanName, this.targetBeanName,是啥?
我cha, 居然又是它的构造的参数, 那这个玩意又是谁跟谁生成来的呢? 真相真的不远了.
再次祭出神器, 看看这个鸟玩意是谁生的?
终于发现是:SecurityFilterAutoConfiguration
没错 ,就是它:private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
他:
cha, 蹂躏它一百遍!!!!
就是它:springSecurityFilterChain, 它真正对应的是谁, 还记得吗???, 往前翻:FilterChainProxy
好, 那最后一个问题: SecurityFilterAutoConfiguration是咋执行的?
让我们再次关注这个文件:
发现org.springframework.boot.autoconfigure.EnableAutoConfiguration的值里面是有SecurityFilterAutoConfiguration的
OK, 那一切都明了!