当前位置:   article > 正文

从源码角度拆解SpringSecurity之万能的SecurityBuilder

securitybuilder

从源码角度拆解SpringSecurity系列文章

从源码角度拆解SpringSecurity之核心流程
从源码角度拆解SpringSecurity之万能的SecurityBuilder
从源码角度拆解SpringSecurity之勤劳的FilterChainProxy
从源码角度拆解SpringSecurity之C位的AuthenticationManager



前言

上一篇我们梳理了整个框架的核心流程,知道了SpringSecurity其实是基于Filter来实现的认证和权限管理,以及它是如何一步步被创建出来的。可是我们使用SpringSecurity框架时最特别的代码就是 “http.xxx().xxx().and().xxx()…” 了吧,这些语句其实就是我们定义的配置,那么这些配置又是如何被加载到框架中的呢,本章我们就一起来进行拆解吧。


注意

文章所摘源码版本为spring-boot-starter-security:5.0.6.RELEASEspring-security-oauth2:2.3.5.RELEASE


一、顶层配置类是怎么注入到IoC的?

上一章讲到构建Filter的配置类实际上是从IoC获取的,即通过 AutowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers方法来进行获取,而这个方法实质上又是从IoC中获取type为WebSecurityConfigurer.class的bean

//从IoC获取顶层配置类
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
				.getBeansOfType(WebSecurityConfigurer.class);
  • 1
  • 2
  • 3

接下来我们回忆下平时开发时的常用配置代码吧

//开启安全配置
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	...
}

//开启认证服务配置
@Configuration
@EnableAuthorizationServer
public class EndpointConfig extends AuthorizationServerConfigurerAdapter {
	...
}

//开启资源配置
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
	...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

第一个是开启SpringSecurity安全框架的必要配置,里面除了注入WebSecurityConfigurer之外还做了其他必要组件的初始化操作,第二个是我们需要使用SpringSecurity为我们封装Endpoint时要开启的配置,第三个是当我们需要对资源访问控制权限做控制时要开启的配置(似乎功能和第一个有重叠,甚至有些时候会造成理解歧义,故后续版本已将 @EnableResourceServer注解标记为过时

继承
实现
SecurityConfig
WebSecurityConfigurerAdapter
WebSecurityConfigurer

SecurityConfig实际上是WebSecurityConfigurer的派生类,而 @EnableWebSecurity 又包含了 @Configuration 注解

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
	boolean debug() default false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

所以这实际上是往IoC注入了类型为WebSecurityConfigurer的bean。
再来看 @EnableAuthorizationServer,它import了 AuthorizationServerSecurityConfiguration配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//导入AuthorizationServerSecurityConfiguration配置类
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {

}

//又继承自WebSecurityConfigurerAdapter,如上文所说,该类是WebSecurityConfigurer的派生类
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
	...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

所以这也相当于是往IoC注入了类型为WebSecurityConfigurer的bean。
同理 @EnableResourceServer也是以同样的逻辑进行注入的,这里就不再赘述。
所以顶层配置有哪些,实际上是取决于我们需要配置哪些,具体配置的来龙去脉这下搞清楚了吧?


二、configurers是怎么来的?

上一章我给大家留了一个小疑问,就是 AbstractConfiguredSecurityBuilder 中的 configurers 是怎么来的,接下来我们就揭晓答案。

	//new出一个空Map等待子配置的加入
	private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
	
	//都是通过add方法往里put的,这是一个私有方法,其调用链均来自apply方法
	private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
		...
		synchronized (configurers) {
			...
			this.configurers.put(clazz, configs);
			...
		}
	}
	
	public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
			throws Exception {
		...
		add(configurer);
		return configurer;
	}

	public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
		add(configurer);
		return configurer;
	}
	
  • 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

所以子配置其实均来自apply方法,还记得上一章讲的WebSecurityConfiguration.setFilterChainProxySecurityConfigurer方法吗,那里面其实就将顶层配置类通过apply方法put进去的,核心代码如下所示

package org.springframework.security.config.annotation.web.configuration;
...
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	
	...
	
	public void setFilterChainProxySecurityConfigurer(
			ObjectPostProcessor<Object> objectPostProcessor,
			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
			throws Exception {
		...
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			webSecurity.apply(webSecurityConfigurer);
		}
		...
	}

	...
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

通过上文我们已经知道顶层配置是类型为WebSecurityConfigurer的bean,而无一例外他们都来自派生类WebSecurityConfigurerAdapter,接下来我们看看这个类到底有何乾坤吧

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer<WebSecurity> {
	...
	
	//如上文所说WebSecurity在执行doBuild时会依次调用顶层配置的init方法和configurer方法
	public void init(final WebSecurity web) throws Exception {
		//先调用getHttp方法生成HttpSecurity
		final HttpSecurity http = getHttp();
		...
	}

	...
	
	protected final HttpSecurity getHttp() throws Exception {
		if (http != null) {
			return http;
		}

		...

		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方法
		configure(http);
		return http;
	}
	
	...
	
	//提供configure的默认实现,该方法一般会被我们覆盖并加入自己的配置
	protected void configure(HttpSecurity http) throws Exception {
		logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
		
		//spring的默认配置,如果不覆写(或覆写后调用super.configure)就会使用该配置
		http
			.authorizeRequests()
				.anyRequest().authenticated()
				.and()
			.formLogin().and()
			.httpBasic();
	}
	
	...
}
  • 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
  • 65
  • 66
  • 67
  • 68
  • 69

这个类比较长,摘取了关键部分(似乎还是比较长

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/67682
推荐阅读
相关标签