阅读完需:约 12 分钟
上一篇笔记讲述了一些关键点和基本的流程,通过上一篇了解了基本的知识为下面做铺垫。
三句话解释框架原理
- 整个框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain类型是FilterChainProxy
- 核心过滤器里面是过滤器链(列表),过滤器链的每个元素都是一组URL对应一组过滤器
- WebSecurity用来创建FilterChainProxy过滤器,
HttpSecurity用来创建过滤器链的每个元素。
1、框架接口设计
关注两个东西:建造者和配置器
框架的用法就是通过配置器对建造者进行配置
框架用法是写一个自定义配置类,继承WebSecurityConfigurerAdapter,重写几个configure()方法
WebSecurityConfigurerAdapter就是Web安全配置器的适配器对象
// 安全建造者
// 顾名思义是一个builder构造器,创建并返回一个类型为O的对象
public interface SecurityBuilder<O> {
O build() throws Exception;
}
// 抽象安全建造者
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
public final O build() throws Exception {
// 限定build()只会进行一次!
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
// 子类需要重写doBuild()方法
protected abstract O doBuild() throws Exception;
}
// 配置后的抽象安全建造者
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
// 实现了doBuild()方法,遍历configurers进行init()和configure()。
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
// 它的子类HttpSecurity和WebSecurity都实现了它的performBuild()方法!!!
protected abstract O performBuild() throws Exception;
// 主要作用是将安全配置器SecurityConfigurer注入到属性configurers中,
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
}
// 安全配置器,配置建造者B,B可以建造O
// 初始化(init)SecurityBuilder,且配置(configure)SecurityBuilder
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
void init(B builder) throws Exception;
void configure(B builder) throws Exception;
}
// Web安全配置器,配置建造者T,T可以建造web过滤器
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>>
extends SecurityConfigurer<Filter, T> {
}
// Web安全配置器的适配器
// 配置建造者WebSecurity,WebSecurity可以建造核心过滤器
public abstract class WebSecurityConfigurerAdapter
implements WebSecurityConfigurer<WebSecurity> {
}
// 用于构建FilterChainProxy的建造者
public final class WebSecurity
extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements
SecurityBuilder<Filter>, ApplicationContextAware {
}
// 用于构建SecurityFilterChain的建造者
public final class HttpSecurity
extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements
SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
}
总结:
-
看到
建造者
去看他的方法,build(); doBuild(); init(); configure(); performBuild();
-
看到
配置器
去看他的方法,init(); config();
从写MySecurityConfig
时使用的@EnableWebSecurity
注解开始看源码:
@EnableWebSecurity
注解导入了三个类,重点关注WebSecurityConfiguration

WebSecurityConfiguration中需要关注两个方法
setFilterChainProxySecurityConfigurer()方法
- 创建了WebSecurity建造者对象,用于后面建造FilterChainProxy过滤器
springSecurityFilterChain()方法
- 调用WebSecurity.build(),建造出FilterChainProxy过滤器对象
————————————————————————————————————
2、FilterChainProxy的创建过程
回顾:
- 框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain,类型是FilterChainProxy
- WebSecurity和HttpSecurity都是建造者
- WebSecurity构建目标是FilterChainProxy对象
- HttpSecurity的构建目标仅仅是FilterChainProxy中的一个SecurityFilterChain。
- @EnableWebSecurity注解,导入了WebSecurityConfiguration类
- WebSecurityConfiguration中创建了建造者对象WebSecurity,和核心过滤器FilterChainProxy
从WebSecurityConfiguration开始
WebSecurityConfiguration中需要关注两个方法:
setFilterChainProxySecurityConfigurer()方法
- 创建了WebSecurity建造者对象,用于后面建造FilterChainProxy过滤器
springSecurityFilterChain()方法
- 调用WebSecurity.build(),建造出FilterChainProxy过滤器对象
WebSecurity的创建过程:setFilterChainProxySecurityConfigurer()方法
该方法负责收集配置类对象列表webSecurityConfigurers,并创建WebSecurity:
- @Value(“#{}”) 是SpEl表达式通常用来获取bean的属性或者调用bean的某个方法。
- 方法执行时,会先得到
webSecurityConfigurers
并排序(所有实现了WebSecurityConfigurerAdapter
的配置类实例) -
new
出websecurity
对象,并使用Spring的容器工具初始化 - 判断
webSecurityConfigurers
内元素的@Order是否有相同,相同的order
会抛异常。
默认order
等于LOWEST_PRECEDENCE = 2147483647
(参考Integer order = AnnotationAwareOrderComparator.lookupOrder(config))
- 将
WebSecurityConfigurerAdapter
的子类apply()
放入websecurity
的List<SecurityConfigurer<O, B>> configurersAddedInInitializing
中。

下图是通过AutowiredWebSecurityConfigurersIgnoreParents
的getWebSecurityConfigurers()
方法,获取所有实现WebSecurityConfigurer
的配置类

FilterChainProxy的创建过程:springSecurityFilterChain()方法
在springSecurityFilterChain()
方法中调用webSecurity.build()
创建了FilterChainProxy
。
PS:根据下面代码,我们可以知道如果创建的MySecurityConfig类没有被sping扫描到,
框架会新new 出一个WebSecurityConfigureAdapter对象,这会导致我们配置的用户名和密码失效。


我们继续看FilterChainProxy
的创建过程:
WebSecurity
是一个建造者,所以我们去看这些方法build(); doBuild(); init(); configure(); performBuild();
build()
方法定义在WebSecurity
对象的父类AbstractSecurityBuilder
中:

build()
方法会调用WebSecurity
对象的父类AbstractConfiguredSecurityBuilder#doBuild()
:

doBuild()
先调用init();configure();
等方法
我们上面已经得知了configurersAddedInInitializing
里是所有的配置类对象
如下图,这里会依次执行配置类的configure();init()
方法

doBuild()最后调用了WebSecurity对象的perfomBuild(),来创建了FilterChainProxy对象
performBuild()里遍历securityFilterChainBuilders建造者列表
把每个SecurityBuilder建造者对象构建成SecurityFilterChain实例
最后创建并返回FilterChainProxy

securityFilterChainBuilders
建造者列表是什么时候初始化的呢
这时候要注意到
WebSecurityConfigurerAdapter
,这个类的创建了HttpSecurity
并放入了securityFilterChainBuilders

WebSecurityConfigurerAdapter是一个安全配置器,我们知道建造者在performBuild()之前都会把循环调用安全配置器的init();configure();方法,然后创建HttpSecurity并放入自己的securityFilterChainBuilders里。
PS: 前面已经提到了,在
WebSecurity
初始化时,会依次将WebSecurityConfigurerAdapter
的子类放入WebSecurity
。

————————————————————————————————————
2、FilterChainProxy的运行过程
我们已经知道了Spring Security
的核心过滤器的创建和原理,本次主要介绍核心过滤器FilterChainProxy
是如何在tomcat的ServletContext
中生效的。
ServletContext如何拿到FilterChainProxy的过滤器对象
我们都知道,Bean都是存在Spring的Bean工厂里的,
而且在Web项目中Servlet、Filter、Listener都要放入ServletContext中。
看下面这张图,ServletContainerInitializer接口提供了一个onStartup()方法,用于在Servlet容器启动时动态注册一些对象到ServletContext中。

官方的解释是:为了支持可以不使用
web.xml
。提供了ServletContainerInitializer
,它可以通过SPI
机制,当启动web容器的时候,会自动到添加的相应jar包下找到META-INF/services
下以ServletContainerInitializer
的全路径名称命名的文件,它的内容为ServletContainerInitializer
实现类的全路径,将它们实例化。
通过下图可知,Spring框架通过META-INF配置了
SpringServletContainerInitializer
类
SpringServletContainerInitializer
实现了ServletContainerInitializer
接口。
请注意该类上的@HandlesTypes(WebApplicationInitializer.class)
注解.
根据Sevlet3.0规范,Servlet容器在调用onStartup()方法时,会以Set集合的方式注入
WebApplicationInitializer
的子类(包括接口,抽象类)。然后会依次调用WebApplicationInitializer
的实现类的onStartup方法,从而起到启动web.xml相同的作用(添加servlet,listener实例到ServletContext中)。

Spring Security中的AbstractSecurityWebApplicationInitializer
就是WebApplicationInitializer
的抽象子类.
当执行到下面的onStartup()
方法时,会调用insertSpringSecurityFilterChain()
将类型为FilterChainProxy
名称为springSecurityFilterChain
的过滤器对象用DelegatingFilterProxy
包装,然后注入ServletContext
DelegatingFilterProxy相关笔记:

运行过程
请求到达的时候,FilterChainProxy的dofilter()方法内部,会遍历所有的SecurityFilterChain,对匹配到的url,则一一调用SecurityFilterChain中的filter做认证或授权。
public class FilterChainProxy extends GenericFilterBean {
private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");
private List<SecurityFilterChain> filterChains;
private FilterChainValidator filterChainValidator = new NullFilterChainValidator();
private HttpFirewall firewall = new StrictHttpFirewall();
public FilterChainProxy() {
}
public FilterChainProxy(SecurityFilterChain chain) {
this(Arrays.asList(chain));
}
public FilterChainProxy(List<SecurityFilterChain> filterChains) {
this.filterChains = filterChains;
}
@Override
public void afterPropertiesSet() {
filterChainValidator.validate(this);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall.getFirewalledResponse((HttpServletResponse) response);
// 根据当前请求,获得一组过滤器链
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest)
+ (filters == null ? " has no matching filters": " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
// 请求依次经过这组滤器链
vfc.doFilter(fwRequest, fwResponse);
}
/**
* 根据Request请求获得一个过滤器链
*/
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
/**
* 根据URL获得一个过滤器链
*/
public List<Filter> getFilters(String url) {
return getFilters(firewall.getFirewalledRequest((new FilterInvocation(url, null)
.getRequest())));
}
/**
* 返回一个过滤器链
*/
public List<SecurityFilterChain> getFilterChains() {
return Collections.unmodifiableList(filterChains);
}
// 过滤器链内部类
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final FirewalledRequest firewalledRequest;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest,
FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
nextFilter.doFilter(request, response, this);
}
}
}
public interface FilterChainValidator {
void validate(FilterChainProxy filterChainProxy);
}
private static class NullFilterChainValidator implements FilterChainValidator {
@Override
public void validate(FilterChainProxy filterChainProxy) {
}
}
}
————————————————————————————————————
4、WebSecurity与HttpSecurity
前面我们已经分析了Spring Security的核心过滤器FilterChainProxy的创建和运行过程,认识了建造者和配置器的作用。
现在我们知道WebSecurity作为一个建造者就是用来创建核心过滤器FilterChainProxy实例的。
WebSecurity在初始化的时候会扫描WebSecurityConfigurerAdapter配置器适配器的子类(即生成HttpSecurity配置器)。
所有的配置器会被调用init();configure();初始化配置,其中生成的每个HttpSecurity配置器都代表了一个过滤器链。
本篇要说的就是HttpSecurity作为一个建造者,是如何去建造出SecurityFilterChain过滤器链实例的!
PS:如果有多个
WebSecurityConfigurerAdapter
配置器适配器的子类,会产生多个SecurityFilterChain
过滤器链实例。Spring Security Oauth2
的拓展就是这么做的。
spring security 怎么创建的过滤器
我们已经知道了springSecurityFilterChain
(类型为FilterChainProxy
)是实际起作用的过滤器链,DelegatingFilterProxy
起到代理作用。
我们创建的MySecurityConfig继承了WebSecurityConfigurerAdapter。WebSecurityConfigurerAdapter就是用来创建过滤器链,重写的configure(HttpSecurity http)的方法就是用来配置HttpSecurity的。
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers() // 指定当前`SecurityFilterChain`实例匹配哪些请求
.anyRequest().and()
.authorizeRequests() // 拦截请求,创建FilterSecurityInterceptor
.anyRequest().authenticated() // 在创建过滤器的基础上的一些自定义配置
.and() // 用and来表示配置过滤器结束,以便进行下一个过滤器的创建和配置
.formLogin().and() // 设置表单登录,创建UsernamePasswordAuthenticationFilter
.httpBasic(); // basic验证,创建BasicAuthenticationFilter
}
上面的configure(HttpSecurity http)
方法内的配置最终内容主要是Filter的创建。
http.authorizeRequests()、http.formLogin()、http.httpBasic()
分别创建了ExpressionUrlAuthorizationConfigurer,FormLoginConfigurer,HttpBasicConfigurer。
在三个类从父级一直往上找,会发现它们都是SecurityConfigurer
建造器的子类。SecurityConfigurer中又有configure()方法。该方法被子类实现就用于创建各个过滤器,并将过滤器添加进HttpSecurity中维护的装有Filter的List中,比如HttpBasicConfigurer中的configure方法,源码如下:
HttpSecurity
作为建造者
会把根据api把这些配置器
添加到实例中



这些配置器中大都是创建了相应的过滤器,并进行配置,最终在HttpSecurity
建造SecurityFilterChain
实例时放入过滤器链
