SpringSecurity 源码分析之SecurityFilterchain的构建

基本原理

spring security通过一系列filter来对请求进行拦截

网上一个比较好的图
SpringSecurity 源码分析之SecurityFilterchain的构建_第1张图片
由于是一个filterChain,因此如果我在FilterSecrutiyInterceptor,即最后一个过滤器上打断点,一定能够通过debug的方式来得到整个过滤器链条
在最后一个filter.doFilter方法上打一个断点,
SpringSecurity 源码分析之SecurityFilterchain的构建_第2张图片
得到整个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
  1. security starter autoconfigure 自动注入的
    SpringBootWebSecurityConfiguration$IgnoredPathsWebSecurityConfigurerAdapter
    SpringBootWebSecurityConfiguration$ApplicationWebSecurityConfigurerAdapter
  2. 用于管理的,暂时没看到在哪个地方注入的
    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类图
    SpringSecurity 源码分析之SecurityFilterchain的构建_第3张图片
    webSecurity.build()

应用的设计思想分析:

  1. 模板方法模式:
    公共的配置如init(),configure()抽象到父类,
    把需要具体对象实现的放到了子类实现.
    如beforeInit(),beforeConfigure();performBuild做为抽象方法,需要具体的对象去实现.
  2. 泛型与策略模式的灵活运用
    虽然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对象,然后进行生成
SpringSecurity 源码分析之SecurityFilterchain的构建_第4张图片
可以看到这里有三个securityFilterChainBuilder,每个securityFilterChainBuilder里面包含configures,sharedObjects等对象
securityFilterChainBuilder.build就是根据这些配置文件来生成filter的,再次调用AbstractConfiguredSecurityBuilder#doBuild如果

整理

  • WebSecurityConfiguration#springSecurityFilterChain
    • WebSecurity#build()
      • AbstractSecurityBuilder#build()->dobuild()
        • AbstractConfiguredSecurityBuilder#doBuild
            1. init()
              遍历所有的configurer,调用init方法
            1. beforeConfigure()
              抽象方法,调用WebSecurity.beforeConfirue()
            1. configure()
              遍历所有的configure,调用configure()方法
            1. performBuild
              抽象方法,调用WebSecurity.performBuild()

上面的流程,第2步与第4步调用WebSecurity这个类的方法,因此最大的变化在第1,3步,里面

BrowserSecurityConfig
//security starter autoconfigure 自动注入的
SpringBootWebSecurityConfiguration I g n o r e d P a t h s W e b S e c u r i t y C o n f i g u r e r A d a p t e r S p r i n g B o o t W e b S e c u r i t y C o n f i g u r a t i o n IgnoredPathsWebSecurityConfigurerAdapter SpringBootWebSecurityConfiguration IgnoredPathsWebSecurityConfigurerAdapterSpringBootWebSecurityConfigurationApplicationWebSecurityConfigurerAdapter
//用于管理的,暂时没看到在哪个地方注入的
ManagementWebSecurityAutoConfiguration M a n a g e m e n t W e b S e c u r i t y C o n f i g u r e r A d a p t e r / / s e c u r i t y s t a r t e r a u t o c o n f i g u r e 自 动 注 入 的 S p r i n g B o o t W e b S e c u r i t y C o n f i g u r a t i o n ManagementWebSecurityConfigurerAdapter //security starter autoconfigure 自动注入的 SpringBootWebSecurityConfiguration ManagementWebSecurityConfigurerAdapter//securitystarterautoconfigureSpringBootWebSecurityConfigurationIgnoredPathsWebSecurityConfigurerAdapter
SpringBootWebSecurityConfiguration A p p l i c a t i o n W e b S e c u r i t y C o n f i g u r e r A d a p t e r / / 用 于 管 理 的 , 暂 时 没 看 到 在 哪 个 地 方 注 入 的 M a n a g e m e n t W e b S e c u r i t y A u t o C o n f i g u r a t i o n ApplicationWebSecurityConfigurerAdapter //用于管理的,暂时没看到在哪个地方注入的 ManagementWebSecurityAutoConfiguration ApplicationWebSecurityConfigurerAdapter//ManagementWebSecurityAutoConfigurationManagementWebSecurityConfigurerAdapter

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);
			}
		});
	}

你可能感兴趣的:(security)