Spring Boot Security原理一(八)

目录

    • 1 概述
      • 1.1 关闭默认Web应用程序安全配置
    • 2 流程分析
      • 2.1 setFilterChainProxySecurityConfigurer()方法
      • 2.2 springSecurityFilterChain()方法
        • 2.2.1 websecurity的build()方法
          • 2.2.1.1 init方法
          • 2.2.1.2 configure方法
          • 2.2.1.3 peformBuild方法
        • 2.2.2 Configurer转换为filter

1 概述

默认Springboot Security安全配置自动加载在SecurityAutoConfiguration和UserDetailsServiceAutoConfiguration。
具体请看spring-boot-autoconfigure-2.0.7.RELEASE.jar下的/META-INF/spring.factories文件
在这里插入图片描述

SecurityAutoConfiguration类:导入SpringBootWebSecurityConfiguration拦截响应的请求;UserDetailsServiceAutoConfiguration类:配置身份验证。

@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
		SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(
			ApplicationEventPublisher publisher) {
		return new DefaultAuthenticationEventPublisher(publisher);
	}

}
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
// 没有AuthenticationManager、AuthenticationProvider和UserDetailsService,加载该bean配置系想你
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
		UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
	....省略
}

1.1 关闭默认Web应用程序安全配置

如果要完全关闭默认Web应用程序安全配置,您可以添加bean继承 WebSecurityConfigurerAdapter,这样Springboot将不会初始化默认的WebSecurityConfigurerAdapter (这样做不会禁用UserDetailsService配置或Actuator的安全性)。
SpringBootWebSecurityConfiguration 的代码如下:

@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
// 没有WebSecurityConfigurerAdapter类或子类加载默认配置
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {

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

	}
}

关闭UserDetailsService的配置,您可以添加类继承 UserDetailsService,AuthenticationProvider或AuthenticationManager,来覆盖Springboot默认实现。
UserDetailsServiceAutoConfiguration 代码如下:

@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
// 没有AuthenticationManager、AuthenticationProvider和UserDetailsService,加载该bean配置系想你
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
		UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
	@Lazy
	public InMemoryUserDetailsManager inMemoryUserDetailsManager(
			SecurityProperties properties,
			ObjectProvider<PasswordEncoder> passwordEncoder) {
		SecurityProperties.User user = properties.getUser();
		List<String> roles = user.getRoles();
		return new InMemoryUserDetailsManager(User.withUsername(user.getName())
				.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
				.roles(StringUtils.toStringArray(roles)).build());
	}
}

2 流程分析

经过上面分析我们知道怎么关闭Springboot默认实现,接着我们看SecurityAutoConfiguration 类中导入的WebSecurityEnablerConfiguration配置信息

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

}
@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 {
	/**
	 * Controls debugging support for Spring Security. Default is false.
	 * @return if true, enables debug support with Spring Security
	 */
	boolean debug() default false;
}

查看源码我们发现WebSecurityConfiguration配置了Security关键信息

@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	private WebSecurity webSecurity;
	private Boolean debugEnabled;
	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
	private ClassLoader beanClassLoader;
	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

	......省略
		
	@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();
	}


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

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

	......省略
}

2.1 setFilterChainProxySecurityConfigurer()方法

开始时会将所有实现SecurityConfigurer接口的类注入到webSecurityConfigurers参数中

@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(
			ObjectPostProcessor<Object> objectPostProcessor,
			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
			throws Exception {
			// 创建webSecurity对象 ,应为是new来的对象,没有走Spring的生命周期,可以利用objectPostProcessor让new来的对象走Spring的生命周期
		webSecurity = objectPostProcessor
				.postProcess(new WebSecurity(objectPostProcessor));
		if (debugEnabled != null) {
			webSecurity.debug(debugEnabled);
		}

		// 主要检验了配置实例的order顺序
		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对象中,其中配置实例中含有我们自定义业务的权限控制配置信息
			webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

该方法中 主要执行如下

 1、创建webSecurity对象

 2、主要检验了配置实例的order顺序(order唯一 否则会报错)

 3、将所有的配置实例存放进入到webSecurity对象中,其中配置实例中含有我们自定义业务的权限控制配置信息

2.2 springSecurityFilterChain()方法

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		// 这个方法会判断我们上一个方法中有没有获取到webSecurityConfigurers,没有的话这边会创建一个WebSecurityConfigurerAdapter实例,并追加到websecurity中
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		// 调用websecurity的build方法
		return webSecurity.build();
	}

该方法中 主要执行如下

 1、这个方法会判断我们上一个方法中有没有获取到webSecurityConfigurers,没有的话这边会创建一个WebSecurityConfigurerAdapter实例,并追加到websecurity中

 2、调用websecurity的build方法

2.2.1 websecurity的build()方法

SecurityBuilder继承关系:
Spring Boot Security原理一(八)_第1张图片
AbstractSecurityBuilder类build()方法

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
	private AtomicBoolean building = new AtomicBoolean();
	private O object;
	
	public final O build() throws Exception {
			if (this.building.compareAndSet(false, true)) {
				this.object = doBuild();
				return this.object;
			}
			throw new AlreadyBuiltException("This object has already been built");
	}
	......省略
}

调用子类AbstractConfiguredSecurityBuilder的doBuild()方法

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
		extends AbstractSecurityBuilder<O> {
	@Override
	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;
		}
	}
	
	protected void beforeInit() throws Exception {
	}
	
	@SuppressWarnings("unchecked")
	private void init() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.init((B) this);
		}

		for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
			configurer.init((B) this);
		}
	}

	protected void beforeConfigure() throws Exception {
	}
	
 	@SuppressWarnings("unchecked")
	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}
	
	......省略
}

build过程主要分三步,init->configure->peformBuild ,具体流程图Spring Boot Security原理一(八)_第2张图片

2.2.1.1 init方法
 1、调用getHttp()方法获取一个http实例,getHttp()方法
	 Ⅰ、添加认证的事件的发布者
	 Ⅱ、配置认证管理器,会调用我们的继承的WebSecurityConfigurerAdapter中重写的
	 		configure(AuthenticationManagerBuilder auth)方法,配置认证相关信息
	 Ⅲ、追加各种SecurityConfigurer的具体实现类到httpSecurity中,如exceptionHandling()方法会追加一个
			ExceptionHandlingConfigurer,sessionManagement()方法会追加一个SessionManagementConfigurer,
	 		securityContext()方法会追加一个SecurityContextConfigurer对象,
	 		这些SecurityConfigurer的具体实现类最终会为我们配置各种具体的filter
	 Ⅳ、最终调用我们的继承的WebSecurityConfigurerAdapter中重写的configure(HttpSecurity http),
	 		将我们业务相关的权限配置规则信息进行初始化操作。默认的configure(HttpSecurity http)方法继续向httpSecurity类中追加	
	 		SecurityConfigurer的具体实现类,如authorizeRequests()方法追加一个ExpressionUrlAuthorizationConfigurer,
	 		formLogin()方法追加一个FormLoginConfigurer。 其中ExpressionUrlAuthorizationConfigurer这个实现类比较重要,
	 		因为他会给我们创建一个非常重要的对象FilterSecurityInterceptor对象,FormLoginConfigurer对象比较简单,
	 		但是也会为我们提供一个在安全认证过程中经常用到会用的一个Filter:UsernamePasswordAuthenticationFilter。

2、通过web.addSecurityFilterChainBuilder方法把获取到的HttpSecurity实例赋值给WebSecurity的securityFilterChainBuilders属性

从init可以看出一个WebSecurity可以生成多个HttpSecurity,而在HttpSecurity又会创建多个拦截器,最终封装到FilterChainProxy对象中
以上三个方法就是WebSecurityConfigurerAdapter类中init方法的主要逻辑,

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer<WebSecurity> {

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

	@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();
		authenticationBuilder.parentAuthenticationManager(authenticationManager);
		authenticationBuilder.authenticationEventPublisher(eventPublisher);
		Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();

		http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
				sharedObjects);
		if (!disableDefaults) {
			// @formatter:off
			// 追加各种SecurityConfigurer的具体实现类到httpSecurity中
			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();
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
		}
		//最终调用我们的继承的WebSecurityConfigurerAdapter中重写的configure(),将我们业务相关的权限配置规则信息进行初始化操作
		configure(http);
		return http;
	}

	protected AuthenticationManager authenticationManager() throws Exception {
		if (!authenticationManagerInitialized) {
			configure(localConfigureAuthenticationBldr);
			if (disableLocalConfigureAuthenticationBldr) {
				authenticationManager = authenticationConfiguration
						.getAuthenticationManager();
			}
			else {
				authenticationManager = localConfigureAuthenticationBldr.build();
			}
			authenticationManagerInitialized = true;
		}
		return authenticationManager;
	}
}

2.2.1.2 configure方法

configure方法最终也调用到会我们继承的WebSecurityConfigurerAdapter中重写的configure(WebSecurity web)方法,默认实现中这个是一个空方法,具体应用中也经常重写这个方法来实现特定需求。

2.2.1.3 peformBuild方法

具体的实现逻辑在WebSecurity类中

遍历securityFilterChainBuilders属性中的SecurityBuilder对象,并调用他的build方法。

这个securityFilterChainBuilders属性我们前面也有提到过(具体请看2.2.1.1 init方法),就是在WebSecurityConfigurerAdapter类的init方法中获取http后赋值给了WebSecurity。因此这个地方就是调用httpSecurity的build方法。
httpSecurity的build方式向其中追加一个个过滤器

@Override
	protected Filter performBuild() throws Exception {
		Assert.state(
				!securityFilterChainBuilders.isEmpty(),
				"At least one SecurityBuilder 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));
		}
		// 遍历securityFilterChainBuilders属性中的SecurityBuilder对象,并调用他的build方法。 
		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;
	}

2.2.2 Configurer转换为filter

peformBuild方法,会在webSecurity类调用httpSecurity的build(具体请看2.2.1.3 peformBuild方法)
httpSecurity也继承于SecurityBuilder,因此也会走build过程,init->configure->peformBuil
在init的时候我们可以看到,会执行这些配置信息,这是在2.2.1.1 init方法中注入的,
Spring Boot Security原理一(八)_第3张图片

在configure方法中,会调用所有的configures,调用该类中的configure方法初始化filter类

@SuppressWarnings("unchecked")
	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

以CsfConfigurer为例

public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
	@SuppressWarnings("unchecked")
	@Override
	public void configure(H http) throws Exception {
	    // 创建Filter并配置
		CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
		RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
		if (requireCsrfProtectionMatcher != null) {
			filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
		}
		AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
		if (accessDeniedHandler != null) {
			filter.setAccessDeniedHandler(accessDeniedHandler);
		}
		LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null) {
			logoutConfigurer
					.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
		}
		SessionManagementConfigurer<H> sessionConfigurer = http
				.getConfigurer(SessionManagementConfigurer.class);
		if (sessionConfigurer != null) {
			sessionConfigurer.addSessionAuthenticationStrategy(
					new CsrfAuthenticationStrategy(this.csrfTokenRepository));
		}
		filter = postProcess(filter);
		http.addFilter(filter);
	}
}

最后再看下HttpSecurity类执行build的最后一步 performBuild,这个方法就是在HttpSecurity中实现的


@Override  
    protected DefaultSecurityFilterChain performBuild() throws Exception {  
        Collections.sort(filters, comparator);  
        return new DefaultSecurityFilterChain(requestMatcher, filters);  
    }  

可以看到,这个类只是把我们追加到HttpSecurity中的security进行了排序,用的排序类是FilterComparator,从而保证我们的filter按照正确的顺序执行。接着将filters构建成filterChian返回。在前面WebSecurity的performBuild方法中,这个返回值会被包装成FilterChainProxy,并作为WebSecurity的build方法的放回值。从而以springSecurityFilterChain这个名称注册到springContext中(在WebSecurityConfiguration中做的)

ExpressionUrlAuthorizationConfigurer的继承关系
ExpressionUrlAuthorizationConfigurer->AbstractInterceptUrlConfigurer->AbstractHttpConfigurer->SecurityConfigurerAdapter->SecurityConfigurer
对应的init方法在SecurityConfigurerAdapter类中,是个空实现,什么也没有做,configure方法在SecurityConfigurerAdapter类中也有一个空实现,在AbstractInterceptUrlConfigurer类中进行了重写

ExpressionUrlAuthorizationConfigurer类图如下所示:
Spring Boot Security原理一(八)_第4张图片

你可能感兴趣的:(springboot)