SpringSecurity从装载到源码

SpringSecurity从装载到源码

前言

​ SpringSecurity从本质来说就是一系列的过滤器链,通过不同的Url路径匹配到不同的过滤器链,之后在其中进行过滤的操作。其整个SpringSecurity完全依赖于Spring容器,而Spring自定义实现的Filter与Servlet原生声明周期又有所不同,因此Spring通过Spring提供了一个GenericFilterBean的实现DelegatingFilterProxy

image-20220511101229638

我们可以将原生的Servlet Filter或者Spring Bean Filter委托给DelegatingFilterProxy,然后在结合到Servlet FilterChain中。

image-20220511094716595

而上述也说到,SpringSecurity中可以通过路径匹配的方式找到不同的过滤器链,也就是实质上存在一揽子过滤器链,那么这个时候实际上由FilterChainProxy来对其进行代理(通过matches()进行匹配选择哪一条过滤器链)代理结构如下所示:

springSecurity.drawio

一、过滤器DelegatingFilterProxy装载ServletContext及被委托过程

先了解一下Java的Spi机制

SPI的全称是Service Provider Interface,是Java提供的可用于第三方实现和扩展的机制,通过该机制,我们可以实现解耦,SPI接口方负责定义和提供默认实现,SPI调用方可以按需扩展.

  1. 在jar包(服务提供者)的META-INF/services/目录中,新建一个文件,文件名为SPI接口的”全限定名”。 文件内容为该接口的具体实现类的”全限定名”

  2. 将spi所在jar放在主程序的classpath中

  3. 服务调用方使用java.util.ServiceLoader去动态加载具体的实现类到JVM中

1.1、DelegatingFilterProxy的装载

​ Servlet容器在启动过程中,增加了ServletContainerInitializer可以在不使用Web.xml配置文件的前提下,通过Spi机制遍历每个jar包中或者web根目录下的META-INF/services目录中的所有javax.servlet.ServletContainerInitializer的文件(其指明了**ServletContainerInitializer的一个实现类),并将其实例化加入Context容器中。当容器启动的时候依次执行其所有ServletContainerInitializer的OnStartUp()方法进行初始化。**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//ServletContainerInitializer接口:

package javax.servlet;

import java.util.Set;

/**
* ServletContainerInitializers (SCIs) are registered via an entry in the
* file META-INF/services/javax.servlet.ServletContainerInitializer that must be
* included in the JAR file that contains the SCI implementation.
* <p>
* SCI processing is performed regardless of the setting of metadata-complete.
* SCI processing can be controlled per JAR file via fragment ordering. If
* absolute ordering is defined, then only the JARs included in the ordering
* will be processed for SCIs. To disable SCI processing completely, an empty
* absolute ordering may be defined.
* <p>
* SCIs register an interest in annotations (class, method or field) and/or
* types via the {@link javax.servlet.annotation.HandlesTypes} annotation which
* is added to the class.
*
* @since Servlet 3.0
*/
public interface ServletContainerInitializer {

/**
* Receives notification during startup of a web application of the classes
* within the web application that matched the criteria defined via the
* {@link javax.servlet.annotation.HandlesTypes} annotation.
*
* @param c The (possibly null) set of classes that met the specified
* criteria
* @param ctx The ServletContext of the web application in which the
* classes were discovered
*
* @throws ServletException If an error occurs
*/
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

​ 针对Spring框架,其也是使用Spi方式实现这个接口,将其加载到容器中。这里的注解@HandlesTypes表示在其注解中标注的所有类都将注入到OnstartUP()方法的参数中。

image-20220511104727679

​ 观察SpringServletContainerInitializer可以发现,其就是通过标注的注解获得所有Spring相关的WebApplicationInitializer初始化器,将其加入到自身的onstartUp()方法中进行执行,最终达到加载不同初始化器中配置的类到servletContext容器当中。官方onStartup()方法解释如下:*If no WebApplicationInitializer implementations are found on the classpath, this method is effectively a no-op. An INFO-level log message will be issued notifying the user that the ServletContainerInitializer has indeed been invoked but that no WebApplicationInitializer implementations were found.Assuming that one or more WebApplicationInitializer types are detected, they will be instantiated (and sorted if the @@Order annotation is present or the Ordered interface has been implemented). Then the WebApplicationInitializer.onStartup(ServletContext) method will be invoked on each instance, delegating the ServletContext such that each instance may register and configure servlets such as Spring’s DispatcherServlet, listeners such as Spring’s ContextLoaderListener, or any other Servlet API componentry such as filters.*大概意思是,如果在类路径没有找WebApplicationInitializer 的实现类,那么这个方法实质上没有起到什么作用,就会发送日志给用户,告知这个ServletContainerInitializer 初始化器已经实际执行到这里,但是没有找到WebApplicationInitializer的相关实现类。如果类路径存在相关实现类,那么这些实现类会被加载到方法中,进行实例化,之后并且按照@Order注解标注的顺序进行排序,最后遍历每一个初始化器执行其中的初始化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//SpringServletContainerInitializer类
package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {

//存储所有从方法参数获得的初始化器
List<WebApplicationInitializer> initializers = Collections.emptyList();

if (webAppInitializerClasses != null) {
initializers = new ArrayList<>(webAppInitializerClasses.size());
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//创建每个初始化器的实例
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}

if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}

servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
//排序所有的初始化器
AnnotationAwareOrderComparator.sort(initializers);
//遍历初始化器集合,按顺序进行加载
for (WebApplicationInitializer initializer : initializers) {
//执行每一个Spring初始化器的初始化方法(向上下文中存入相应对象)
initializer.onStartup(servletContext);
}
}

}

​ 那么当容器初始化的时候就会注入Spring的初始化器。根据Spring初始化启动器加载又会加载@HandlesTypes标注的所有相应的WebApplicationInitializers。那么这里我们关注SpringSecurity中的这个初始化器AbstractSecurityWebApplicationInitializer,根据第一行Registers the DelegatingFilterProxy to use the springSecurityFilterChain before any other registered Filter说到:会在注册其他过滤器前,实际会注册DelegatingFilterProxy来使用springSecurityFilterChain。

image-20220511110533558

​ 这个时候我们关注onstartUP()方法实际执行过程。这里我们注意insertSpringSecurityFilterChain()方法的执行,根据方法执行来看首先获取一个filterName,发现其是名字就是“springSecurityFilterChain”,之后将这个DelegatingFilterProxy注册到容器中去。完成这个DelegatingFilterProxy加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//AbstractSecurityWebApplicationInitializer类

@Override
public final void onStartup(ServletContext servletContext) {
//在SpringSecurity过滤器加载之前执行的方法(这里是空方法由子类实现)
beforeSpringSecurityFilterChain(servletContext);
//判断当前是否存在其他的配置类
if (this.configurationClasses != null) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(this.configurationClasses);
servletContext.addListener(new ContextLoaderListener(rootAppContext));
}
//通过覆盖enableHttpSessionEventPublisher()方法来决定是否开启HttpSessionEventPublisher,默认禁用
if (enableHttpSessionEventPublisher()) {
servletContext.addListener("org.springframework.security.web.session.HttpSessionEventPublisher");
}
servletContext.setSessionTrackingModes(getSessionTrackingModes());
//实际插入DelegatingFilterProxy
insertSpringSecurityFilterChain(servletContext);
//配置过滤器链之后执行的方法(这里是空方法由子类实现)
afterSpringSecurityFilterChain(servletContext);
}

/**
* 注册过滤器链
* Registers the springSecurityFilterChain
* @param servletContext the {@link ServletContext}
*/
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
String filterName = DEFAULT_FILTER_NAME;
//调用其构造方法将这个springSecurityFilterChain目标要代理的名字设置为其成员变量targetBeanName
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(filterName);
String contextAttribute = getWebApplicationContextAttribute();
if (contextAttribute != null) {
springSecurityFilterChain.setContextAttribute(contextAttribute);
}
registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}

configurationClasses(onStartup()中的调用this.configurationClasses != null):private final Class<?>[] configurationClasses;这里根据文档注释可以发现这个类的初始化构造方法存在两个一个加载了一个配置类到其成员方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//AbstractSecurityWebApplicationInitializer类
//两个构造方法

/**
* 创建实例时,假设Spring安全配置是通过此类之外的类进行加载。
* AbstractSecurityWebApplicationInitializer类实际上是在SpringServletContainerInitializer使用反射工具加载的 ReflectionUtils.accessibleConstructor(waiClass).newInstance());因此调用的是当前无参构造
* Creates a new instance that assumes the Spring Security configuration is loaded by
* some other means than this class. For example, a user might create a
* {@link ContextLoaderListener} using a subclass of
* {@link AbstractContextLoaderInitializer}.
*
* @see ContextLoaderListener
*/
protected AbstractSecurityWebApplicationInitializer() {
this.configurationClasses = null;
}

/**
* 使用指定的类实例化当前初始化器
* Creates a new instance that will instantiate the {@link ContextLoaderListener} with
* the specified classes.
* @param configurationClasses
*/
protected AbstractSecurityWebApplicationInitializer(Class<?>... configurationClasses) {
this.configurationClasses = configurationClasses;
}

1.2、DelegatingFilterProxy被FilterChainProxy委托过程

​ 当容器加载这个过滤器的时候执行过滤器的初始化方法(init()),但是由于GenericFilterBean实现了这个init()方法并且在其中调用了initFilterBean()这个方法(空实现,子类实现)而当前DelegatingFilterProxy实现了GenericFilterBean的initBean()方法,因此其这个initFilterBean()方法会被实际执行。执行的过程就是获取需要代理的FilterName(这里名字是上面初始化设置的springSecurityFilterChain),最后从IOC容器中获取这个需要被代理的对象,实际上就是FilterChainProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//DelegatingFilterProxy类
//初始化过滤器
@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);
}
}
}
}

二、过滤请求执行流程

2.1、DelegatingFilterProxy代理FilterChainProxy执行

​ 打开DelegatingFilterProxydoFilter()方法,在执行过程中,首先会获取其中在初始化时候配置的委托对象,之后实际执行代理方法,也就是执行被委托对象的过滤方法doFilter(),这里也就是执行了FilterChainProxy的doFilter()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//DelegatingFilterProxy类
//过滤方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}

// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
//实际执行代理方法
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

delegate.doFilter(request, response, filterChain);
}

2.2、FilterChainProxy分发请求到securityFilterChain

​ 根据上述,请求通过DelegatingFilterProxy类的代理之后,最终实际执行FilterChainProxydoFilter()方法,根据如下源码进行分析:发现在执行doFilter()的过程中实际上执行的是其doFilterInternal方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//FilterChainProxy类
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (!clearContext) {
doFilterInternal(request, response, chain);
return;
}
try {
//实际执行在这里
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
}
catch (RequestRejectedException ex) {
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}

​ 在doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)方法中发现有一个getFilters方法,跳转到方法中可以知道其会在所有的SecurityFilterChain中进行Url地址匹配并且最终返回匹配的过滤器链,在拿到上面所有的过滤器链之后判断其是否存在过滤器链,不存在就进行放行执行其他的过滤器链。存在的话就会使用获取的过滤器链中的所有过滤器构建虚拟过滤器,并且执行虚拟过滤器的doFilter方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//FilterChainProxy类
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
//首先会获取所有的过滤器链
List<Filter> filters = getFilters(firewallRequest);
//不存在SecurityFilterChain过滤器链就进行放行
if (filters == null || filters.size() == 0) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
}
firewallRequest.reset();
//执行容器中其他的过滤器
chain.doFilter(firewallRequest, firewallResponse);
return;
}
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
}
//执行虚拟过滤器链
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}

/**
* Returns the first filter chain matching the supplied URL.
* @param request the request to match
* @return an ordered array of Filters defining the filter chain
*/
private List<Filter> getFilters(HttpServletRequest request) {
int count = 0;
for (SecurityFilterChain chain : this.filterChains) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, ++count,
this.filterChains.size()));
}
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}

​ 观察VirtualFilterChain(虚拟过滤器链)这个类,其中包含了原始的过滤器链FilterChain,以及实际的SecurityFilterChain过滤器链,在执行过滤的过程中,拿到下一个需要执行的过滤器然后执行nextFilter.doFilter(request, response, this);这里的this就是当前虚拟的过滤器链,之后会一直执行doFilter(ServletRequest request, ServletResponse response) -> 获取下一个过滤器->执行nextFilter.doFilter(request, response, this);直到SecurityFilterChain过滤器链被执行结束,然后执行其他的容器中的原始过滤器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//VirtualFilterChain类	
/**
* Internal {@code FilterChain} implementation that is used to pass a request through
* the additional internal list of filters which match the request.
*/
private static final class VirtualFilterChain implements FilterChain {

//原生请求的过滤器链
private final FilterChain originalChain;

//需要进行执行实际的SecurityFilterChain过滤器链
private final List<Filter> additionalFilters;

//实际上将原生请求进行包装HttpServletRequestWrapper的子类
private final FirewalledRequest firewalledRequest;

//SecurityFilterChain过滤器链中过滤器的数量
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 {

//SecurityFilterChain过滤器链被执行结束
if (this.currentPosition == this.size) {
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));
}
// Deactivate path stripping as we exit the security filter chain
// 执行原生过滤器链,安全放行通过
this.firewalledRequest.reset();
this.originalChain.doFilter(request, response);
return;
}
this.currentPosition++;
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),
this.currentPosition, this.size));
}
nextFilter.doFilter(request, response, this);
}

}

源码执行流程:

image-20220511123453301

三、FilterProxy创建过程

建造者

  1. WebSecurity用来创建FilterChainProxy过滤器
  2. HttpSecurity用来创建过滤器链的每个元素。

3.1、@EnableWebSecurity注解

image-20220511142412009

  1. 使用@Import注解导入
    1. WebSecurityConfiguration.class:使用WebSecurity创建对应的FilterChainPorxy对象 -> 可以通过继承WebSecurityConfigurerAdapter来实现自定义配置WebSecurity。
    2. SpringWebMvcImportSelector.class:使其在开启WebSecurity的时候有条件的导入WebMvcSecurityConfiguration配置。
    3. OAuth2ImportSelector.class:当引入oauth2模块,Spring会自动启用 OAuth2 客户端配置 OAuth2ClientConfiguration。
    4. HttpSecurityConfiguration.class:实际上是构建一个默认的HttpSecurity实例对象。
  2. 开启全局授权管理@EnableGlobalAuthentication:导入了AuthenticationConfiguration用于全局认证机制配置
    1. AuthenticationConfiguration主要目的用于配置认证管理器组件AuthenticationManager
    2. AuthenticationManager会在运行时用于认证请求者身份。

3.2、WebSecurityConfiguration

​ 可以知道SpringSecurity实际上是建造者配置器 进行实际创建的,这里建造者就是HttpSecurity和WebSecurity,而下图就是实际的配置器,通过下述配置器获取HttpSecurity建造者实际对一条过滤器链进行构建。

配置器

3.2.1、setFilterChainProxySecurityConfigurer()

1. 在这个方法中首先通过@Value注解从容器中获取所有`WebSecurityConfigurerAdapter`的实现类
    2. 创建webSecurity实例对象,使用Spring容器初始化
    3. 对其`WebSecurityConfigurerAdapter`所有实现类进行升序排序
    4. 收集所有的WebSecurityConfigurer配置对象
    5. apply(webSecurityConfigurer) -> 实际上是父类AbstractConfiguredSecurityBuilder提供的add方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//WebSecurityConfiguration
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//创建webSecurity实例对象,使用Spring容器初始化
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
//对其实现的所有的webSecurity进行排序
webSecurityConfigurers.sort(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;
}
//收集所有的WebSecurityConfigurer配置对象,排序后的所有WebSecurityConfigurer依次添加到WebSecurity中
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
this.webSecurity.apply(webSecurityConfigurer);
}
//保存排序后的所有WebSecurityConfigurer
this.webSecurityConfigurers = webSecurityConfigurers;
}

AbstractConfiguredSecurityBuilder中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//AbstractConfiguredSecurityBuilder类

/**
* Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any
* {@link SecurityConfigurer} of the exact same class. Note that object hierarchies
* are not considered.
* @param configurer
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
*/
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
//调用方法将配置进行添加
add(configurer);
return configurer;
}


/**
* 根据配置类状态进行增加
* Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking
* {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary.
* @param configurer the {@link SecurityConfigurer} to add
*/
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (this.configurers) {
//当前是否已经配置过,配置过不进行配置
if (this.buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
}
List<SecurityConfigurer<O, B>> configs = null;
if (this.allowConfigurersOfSameType) {
configs = this.configurers.get(clazz);
}
configs = (configs != null) ? configs : new ArrayList<>(1);
configs.add(configurer);
this.configurers.put(clazz, configs);
if (this.buildState.isInitializing()) {
//判断其需要进行初始化的配置对象SecurityConfigurer ,将其加入configurersAddedInInitializing
//private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
this.configurersAddedInInitializing.add(configurer);
}
}
}

因此综上所描述,在这个setFilterChainProxySecurityConfigurer()方法中实质上就是从容器中获取这些SecurityConfigurer,之后给WebSecurity对象增加一系列的按照顺序排列好的初始化配置类对象(SecurityConfigurer类的子类对象 -> WebSecurityConfigurerAdapter)

3.2.2、springSecurityFilterChain()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//WebSecurityConfiguration

/**
* Creates the Spring Security Filter Chain
* @return the {@link Filter} that represents the security filter chain
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
Assert.state(!(hasConfigurers && hasFilterChain),
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
//这里是关键,真正开始构建其FilterChainProxy
return this.webSecurity.build();
}

​ 在这个方法中实际上对其WebSecurity进行构建,也就是构建FilterChainProxy,查看build()源码,可以发现以下流程,首先在WebSecurityspringSecurityFilterChain()方法中,最后调用构建build()方法,实际上使用其父类的父类AbstractSecurityBuilder中的build()方法,在这个build()方法中调用了dobuild()方法,这个doBuild()方法实际上是尤其子类进行实现的因此调用的是AbstractConfigureSecurityBuilderdoBuild()方法,在这个里面调用performBuild()方法实际上由webSecurity实现。

image-20220511153214607

​ 从上面来看其实际上是执行了,对于每个WebSecurityConfig里面进行一些初始化以及初始化的设置,之后最后通过performBuild() 进行真正的构建,在这个方法中关注两个For循环,第一个循环之将需要忽略的请求路径所构成的RequestMatcher构建成为一条过滤器链,加入集合。第二个循环中实际上是遍历所有securityFilterChainBuilders过滤器链构建器来创建过滤器链,并将其加入过滤器链集合。但是这个构建器是什么时候放入WebbSecurity的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//WebSecurity类
@Override
protected Filter performBuild() throws Exception {
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this is done by exposing a SecurityFilterChain bean "
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
//初始化设置一个SecurityFilterChain集合,包含所有过滤器链
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
//构建需要忽略Security的过滤器链
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
//遍历所有securityFilterChainBuilders构建者列表
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
//调用构建者的build()方法构建实例
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);
}
//构建过滤器链代理
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
filterChainProxy.afterPropertiesSet();
//将这个构建好的过滤器代理返回
Filter result = filterChainProxy;
if (this.debugEnabled) {
this.logger.warn("\n\n" + "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
this.postBuildAction.run();
//返回FilterChainProxy
return result;
}

​ 根据上图可以发现,在将这个所有配置器WebSecurityConfigurerAdapter放入webSecurity的过程是由3.2.1、setFilterChainProxySecurityConfigurer()完成的,之后在执行springSecurityFilterChain() -> build()方法的时候发现其会对每一个WebSecurityConfigurerAdapter配置器进行初始化也就是执行他的init()方法。在这个方法中会使用这个配置器进行创建httpSecurity对象之后将其放入WebSecurity的构建器中也就是securityFilterChainBuilders,最后在上面performBuild()方法中构建过滤器链。也印证了开头说到的:

  1. WebSecurity用来创建FilterChainProxy过滤器
  2. HttpSecurity用来创建过滤器链的每个元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//AbstractConfiguredSecurityBuilder实际上是当前的WebSecurity对象
@SuppressWarnings("unchecked")
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
//this就是WebSecurity
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
configurer.init((B) this);
}
}

//WebSecurityConfigurerAdapter
@Override
public void init(WebSecurity web) throws Exception {
//构建httpSecurity
HttpSecurity http = getHttp();
//将其SecurityFilterChainBuilder加入WebSecurity中
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}

最终,通过一系列初始化,从配置器构建建造者,再由建造者构建过滤器链,最后由WebSecurity构建FilterChainProxy,将其注入返回到容器中。

image-20220511161229950


以上为个人总结的SpringSecurity过滤器链的创建和加载过程,如有错误欢迎指正

:satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied::satisfied:

原创内容转载请声明来源

参考文章:

  1. 深入浅出Spring Security

  2. Spring Security过滤器链体系

  3. Spring Security的配置

  4. Spring Security 实战干货:图解Spring Security的过滤器体系

  5. Spring Security 源码分析八:Spring Security 过滤链一 - 概念与设计

  6. (拦截器、过滤器)之扯不断理还乱

  7. Spring Security源码