Spring-Security框架的启动过程

news/2024/6/15 17:36:42 标签: spring, java, servlet, security

springboot的spring.factories配置文件中通过org.springframework.boot.autoconfigure.EnableAutoConfiguration配置项配置了SecurityAutoConfiguration为自动加载的配置类,可以看到通过@Import注解导入SpringBootWebSecurityConfiguration、WebSecurityEnablerConfiguration、SecurityDataConfiguration三个组件。

java">@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
		SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {......}

SpringBootWebSecurityConfiguration组件要做的事情是当引入了Security框架(WebSecurityConfigurerAdapter类存在)且没有定制时(WebSecurityConfigurerAdapter类型Bean不存在),创建默认的配置DefaultConfigurerAdapter。

java">@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {

	@Configuration(proxyBeanMethods = false)
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

	}

}

不要被这么多注解吓到,仔细得看其实没有什么,核心就是通过@EnableWebSecurity导入WebSecurityConfiguration、SpringWebMvcImportSelector、OAuth2ImportSelector组件。

java">@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {

}
......
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

	boolean debug() default false;
}

setFilterChainProxySecurityConfigurer()方法上存在@Autowired注解,因此会被调用,会向方法注入objectPostProcessor(AutowireBeanFactoryObjectPostProcessor)和webSecurityConfigurers(SecurityConfigurer接口的实现类Bean,即我们业务中的WebSecurityConfigurerAdapter的扩展子类)。

java">// WebSecurityConfiguration

	private WebSecurity webSecurity;
	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

    @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));
		if (debugEnabled != null) {
			webSecurity.debug(debugEnabled);
		}

		......
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

AutowireBeanFactoryObjectPostProcessor#postProcess()方法会调用Bean得初始化方法,对属性进行填充,即对WebSecurity进行实例化。

java">	public <T> T postProcess(T object) {
		if (object == null) {
			return null;
		}
		T result = null;
		try {
			result = (T) this.autowireBeanFactory.initializeBean(object,
					object.toString());
		}
		catch (RuntimeException e) {
			Class<?> type = object.getClass();
			throw new RuntimeException(
					"Could not postProcess " + object + " of type " + type, e);
		}
		this.autowireBeanFactory.autowireBean(object);
		if (result instanceof DisposableBean) {
			this.disposableBeans.add((DisposableBean) result);
		}
		if (result instanceof SmartInitializingSingleton) {
			this.smartSingletons.add((SmartInitializingSingleton) result);
		}
		return result;
	}

再就是通过WebSecurity的AbstractConfiguredSecurityBuilder#apply()方法保存配置到configurers(private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();),configurers的key为SecurityConfigurer或子类,value为对应的实现类对象,也就是在这里保存我们业务中实现的WebSecurityConfigurerAdapter的扩展子类。

java">	public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
		add(configurer);
		return configurer;
	}
	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 (configurers) {
			if (buildState.isConfigured()) {
				throw new IllegalStateException("Cannot apply " + configurer
						+ " to already built object");
			}
            //allowConfigurersOfSameType默认为false
			List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
					.get(clazz) : null;
			if (configs == null) {
				configs = new ArrayList<>(1);
			}
			configs.add(configurer);
			this.configurers.put(clazz, configs);
			if (buildState.isInitializing()) {
				this.configurersAddedInInitializing.add(configurer);
			}
		}
	}

至此spring将我们定义的WebSecurityConfigurerAdapter扩展保存下来了,接下来会通过WebSecurity的父类AbstractSecurityBuilder#build()构建对象。

java">	@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();
	}

核心在AbstractConfiguredSecurityBuilder#doBuild()方法,看下doBuild()方法的过程吧。

java">	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;
		}
	}
  1. 对AuthenticationManagerBuilder进行配置protected void configure(AuthenticationManagerBuilder auth),实例化HttpSecurity,并调用钩子函数protected void configure(HttpSecurity http)对HttpSecurity进行定制,我们业务代码中的两个config就是在这里被调用的。
java">	public void init(final WebSecurity web) throws Exception {
		final HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
			FilterSecurityInterceptor securityInterceptor = http
					.getSharedObject(FilterSecurityInterceptor.class);
			web.securityInterceptor(securityInterceptor);
		});
	}
    protected final HttpSecurity getHttp() throws Exception {
		if (http != null) {
			return http;
		}

		DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
				.postProcess(new DefaultAuthenticationEventPublisher());
		localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

		AuthenticationManager authenticationManager = authenticationManager();
		authenticationBuilder.parentAuthenticationManager(authenticationManager);
		authenticationBuilder.authenticationEventPublisher(eventPublisher);
		Map<Class<?>, Object> sharedObjects = createSharedObjects();

		http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
				sharedObjects);
		if (!disableDefaults) {
			// @formatter:off
			http
				.csrf().and()
				.addFilter(new WebAsyncManagerIntegrationFilter())
				.exceptionHandling().and()
				.headers().and()
				.sessionManagement().and()
				.securityContext().and()
				.requestCache().and()
				.anonymous().and()
				.servletApi().and()
				.apply(new DefaultLoginPageConfigurer<>()).and()
				.logout();
			// @formatter:on
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
		}
		configure(http);
		return http;
	}
  1. 对FilterChainProxy进行实例化,FilterChainProxy封装了过滤器链
java">	protected Filter performBuild() throws Exception {
		Assert.state(
				!securityFilterChainBuilders.isEmpty(),
				() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
						+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
						+ "More advanced users can invoke "
						+ WebSecurity.class.getSimpleName()
						+ ".addSecurityFilterChainBuilder directly");
		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);
		if (httpFirewall != null) {
			filterChainProxy.setFirewall(httpFirewall);
		}
		filterChainProxy.afterPropertiesSet();

		Filter result = filterChainProxy;
		if (debugEnabled) {
			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);
		}
		postBuildAction.run();
		return result;
	}

最终springSecurityFilterChain()方法返回的就是FilterChainProxy对象。继续看,在SecurityFilterAutoConfiguration配置类中通过Bean方法创建了DelegatingFilterProxyRegistrationBean组件。

java">	@Bean
	@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
	public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
			SecurityProperties securityProperties) {
		DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
				DEFAULT_FILTER_NAME);
		registration.setOrder(securityProperties.getFilter().getOrder());
		registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
		return registration;
	}

DelegatingFilterProxyRegistrationBean的父类RegistrationBean实现了ServletContextInitializer接口的onStartup()方法。

java">// RegistrationBea
	public final void onStartup(ServletContext servletContext) throws ServletException {
		String description = getDescription();
		if (!isEnabled()) {
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
		register(description, servletContext);
	}

首先调用DelegatingFilterProxyRegistrationBean的父类AbstractFilterRegistrationBean#getDescription()方法,其实就是将targetBeanName注入到DelegatingFilterProxy中,this.targetBeanName的默认值为"springSecurityFilterChain",即我们调用springSecurityFilterChain()方法创建的FilterChainProxy对象的name。

java">// AbstractFilterRegistrationBean
	protected String getDescription() {
		Filter filter = getFilter();
		Assert.notNull(filter, "Filter must not be null");
		return "filter " + getOrDeduceName(filter);
	}
// DelegatingFilterProxyRegistrationBean
    public DelegatingFilterProxy getFilter() {
		//targetBeanName="springSecurityFilterChain"
		return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {

			@Override
			protected void initFilterBean() throws ServletException {
				// Don't initialize filter bean on init()
			}

		};
	}

回到onStartup()方法中的register()方法调用,其实就是调用AbstractFilterRegistrationBean#addRegistration()方法注册过滤器到servlet上下文,即将DelegatingFilterProxy过滤器添加到tomcat容器中。

java">// DynamicRegistrationBean.java
	protected final void register(String description, ServletContext servletContext) {
		D registration = addRegistration(description, servletContext);
		if (registration == null) {
			logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
			return;
		}
		configure(registration);
	}
// AbstractFilterRegistrationBean.java 
	protected Dynamic addRegistration(String description, ServletContext servletContext) {
		Filter filter = getFilter();
		return servletContext.addFilter(getOrDeduceName(filter), filter);
	}

总结一下,spring通过自动配置导入WebSecurityConfigurerAdapter扩展,然后实例化HttpSecurity,使用扩展对HttpSecurity进行配置,得到 完成配置后注入到DelegatingFilterProxy中,由DelegatingFilterProxyRegistrationBean添加到tomcat容器中,这样就能通过过滤器DelegatingFilterProxy找到FilterChainProxy完成过滤器的调用。有不对的地方请大神指出,欢迎大家一起讨论交流,共同进步。


http://www.niftyadmin.cn/n/177595.html

相关文章

【算法竞赛-初级】基础数据结构-链表篇

这个系列的文章主要是用来记录系统学习算法的心得思路&#xff0c;主要参考书籍是罗勇军老师的算法竞赛。现在目前目标是完成该书的初级部分&#xff0c;这篇文章是第一章基本数据结构中的链表部分。后续将持续更新该系列&#xff0c;欢迎大家一键三连&#xff01;&#x1f601…

YOLOV5详解

1. YOLOV5的前处理Anchor的改进 1.1 Anchor生成的改进 首先YOLOV3/V4/V5都是根据训练的数据集来生成anchor, 就是在训练之前用一个独立的程序去计算Anchor, 但是还不够好 因为自动生成的anchor是拿来整个数据集去做的&#xff0c;但是我们知道目标检测训练的时候是分batch训练…

Linux--数据链路层--ARP协议--0319-21

目录 1. 认识以太网 1.1 以太网帧格式 1.2 基于以太网帧简单模拟局域网通信 问题一&#xff1a;如果有多台主机都在发送数据呢&#xff1f; 问题二&#xff1a;发送方知不知道自己的数据被影响了呢&#xff1f; 1.3 MTU 1.3.1 MTU对IP协议的影响 1.3.2 MTU对UDP协议的影…

《计算机网络-自顶向下》02. 应用层

文章目录应用层协议原理应用程序体系结构进程通信客户和服务器进程进程与计算网络之间的接口进程寻址可供应用使用的运输层服务可靠数据传输吞吐量定时安全性因特网提供的运输服务TCP安全的 TCPUDP因特网运输协议所不提供的服务一些因特网应用所使用的运输层协议网络应用 —— …

用于电力电子器件的栅极驱动器

栅极驱动器是一种功率放大器&#xff0c;它接受来自控制器IC的低功耗输入&#xff0c;并为功率器件产生适当的高电流栅极驱动。随着对电力电子器件的要求不断提高&#xff0c;栅极驱动器电路的设计和性能变得越来越重要。 功率半导体器件是现代电力电子系统的核心。这些系统利…

【C#】给容器里控件批量初始化

系列文章 【C#】单号生成器&#xff08;定义编号规则、流水号、产生业务单号&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129129787 【C#】日期范围生成&#xff08;构建本周开始、结束日期&#xff09; 本文链接&#xff1a;https…

CentOS 7 安装 mysql 8.0

官方文档 2.5.1 Installing MySQL on Linux Using the MySQL Yum Repository 官网查找最新版本 MySQL Product Archives 复制这个链接地址&#xff0c;并下载 wget https://repo.mysql.com//mysql80-community-release-el7-7.noarch.rpm 接下来&#xff0c;按照官方文档步…

天梯赛练习集--L1-001到L1-010--python - java

python L1-001 Hello World print("Hello World!")L1-002 打印沙漏 a input().split() n int(a[0]) c 1 while(2*c**2-1<n):c1 c-1 b 2*c - 1 for i in range(c):print(" "*ia[1]*(b-2*i)) for i in range(1,c):print(" "*(c-i-1)a[1]…