SpringBoot2整合SpringSecurity+Swagger3(源码分析三)


SpringBoot2整合SpringSecurity+Swagger3系列


org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
在这里插入图片描述
当WebSecurityConfigurerAdapter类型Bean创建的过程中,会进行自动注入操作。以下方法执行Spring容器对象的注入操作,注册Spring容器对象的同时,会获取ObjectPostProcessor(用于一个对象在Spring容器中的初始化操作,在适当时机调用Aware、afterPropertiesSet、destroy方法),创建一个懒加载的密码编码器(此时设置Spring容器,只有当真实加密的时候才回去从容器中获取一个密码编码器的实现,本质上这个拉加载的密码加密器就是一个代理),设置认证构造器对象。

@Autowired
public void setApplicationContext(ApplicationContext context) {
	this.context = context;

	ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
	LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);

	authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
	localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
		@Override
		public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
			authenticationBuilder.eraseCredentials(eraseCredentials);
			return super.eraseCredentials(eraseCredentials);
		}

	};
}
  • 对象处理器

首先从容器中获取ObjectPostProcessor实例,默认情况下实现为AutowireBeanFactoryObjectPostProcessor,创建这个类的时候传入了Spring工厂实例,这样就与Spring发生了联系,针对某个对象进行处理的时候调用的方法如下

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

不难看出就是针对这个类型为T的对象执行初始化方法(比如这个对象实现了Spring的InitializingBean的方法,在autowireBeanFactory.initializeBean的时候就会执行afterPropertiesSet方法),然后就是注入操作(也就是类型为T的对象中包含了比如Autowired注解,就会自动注入)。其实就是将原本由Spring容器自动完成的Bean创建过程变为手动调用postProcess来完成。如果这个对象实现了DisposableBean、SmartInitializingSingleton接口,还会保存到本地。因为AutowireBeanFactoryObjectPostProcessor这个类额外实现了DisposableBean和SmartInitializingSingleton,所以当Spring容器完成初始化或者注销的时候,会通过观察者模式调用AutowireBeanFactoryObjectPostProcessor的afterSingletonsInstantiated方法和destroy方法,这个时候就可以将上面在postProcess方法中收集的对象执行对应的方法(先收集后面遍历执行操作而已)。如下所示

@Override
public void afterSingletonsInstantiated() {
	for (SmartInitializingSingleton singleton : smartSingletons) {
		singleton.afterSingletonsInstantiated();
	}
}

public void destroy() throws Exception {
	for (DisposableBean disposable : this.disposableBeans) {
		try {
			disposable.destroy();
		}
		catch (Exception error) {
			this.logger.error(error);
		}
	}
}
  • 懒加载密码编码器

这里的懒加载并不是包含注解@Lazy,只是一个代理,在真实编码的时候才去容器中获取真实的密码编码器。

@Override
public String encode(CharSequence rawPassword) {
	return getPasswordEncoder().encode(rawPassword);
}

private PasswordEncoder getPasswordEncoder() {
	if (this.passwordEncoder != null) {
		return this.passwordEncoder;
	}
	PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
	if (passwordEncoder == null) {
		passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
	this.passwordEncoder = passwordEncoder;
	return passwordEncoder;
}

private <T> T getBeanOrNull(Class<T> type) {
	try {
		return this.applicationContext.getBean(type);
	} catch(NoSuchBeanDefinitionException notFound) {
		return null;
	}
}

所以在创建的时候需要设置Spring容器。而真实使用的则是容器当中注入的,比如

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
  • 认证管理器构造器

创建一个默认的认证管理构造器,传入前面创建的对象处理器和加密编码器。

protected AbstractConfiguredSecurityBuilder(
		ObjectPostProcessor<Object> objectPostProcessor,
		boolean allowConfigurersOfSameType) {
	Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
	this.objectPostProcessor = objectPostProcessor;
	this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}

DefaultPasswordEncoderAuthenticationManagerBuilder(
	ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) {
	super(objectPostProcessor);
	this.defaultPasswordEncoder = defaultPasswordEncoder;
}

当完成ApplicationContext注入后,继续为WebSecurityConfigurerAdapter注入其他Bean,比如objectPostProcessor、contentNegotiationStrategy以及authenticationConfiguration,其中authenticationConfiguration会复杂一点。创建AuthenticationConfiguration类型Bean会注入GlobalAuthenticationConfigurerAdapter类型的Bean,默认情况下有是三个,都是AuthenticationConfiguration的静态内部类。

@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
		List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
	Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
	this.globalAuthConfigurers = configurers;
}

关系如下所示

"authenticationConfiguration" -> org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration -> 
    注入SecurityConfigurer类型Bean(setGlobalAuthenticationConfigurers进行注入)
	0 = "enableGlobalAuthenticationAutowiredConfigurer" (GlobalAuthenticationConfigurerAdapter)
	1 = "initializeUserDetailsBeanManagerConfigurer" (InitializeUserDetailsBeanManagerConfigurer) 
	2 = "initializeAuthenticationProviderBeanManagerConfigurer" (InitializeAuthenticationProviderBeanManagerConfigurer)

在这里插入图片描述

securityConfig -> 
	0 = "objectPostProcessor"
	1 = "trustResolver"
	2 = "contentNegotationStrategy"
	3 = "applicationContext"
	4 = "authenticationConfiguration" -> org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration -> 
		注入SecurityConfigurer类型Bean(setGlobalAuthenticationConfigurers进行注入)
		0 = "enableGlobalAuthenticationAutowiredConfigurer" (GlobalAuthenticationConfigurerAdapter)
		1 = "initializeUserDetailsBeanManagerConfigurer" (InitializeUserDetailsBeanManagerConfigurer) 
		2 = "initializeAuthenticationProviderBeanManagerConfigurer" (InitializeAuthenticationProviderBeanManagerConfigurer)

创建authenticationManagerBuilder

@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
		ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
	LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
	AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);

	DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
	if (authenticationEventPublisher != null) {
		result.authenticationEventPublisher(authenticationEventPublisher);
	}
	return result;
}

懒加载的密码加密器之前已经介绍过,AuthenticationEventPublisher用户没定义的话,则使用默认的DefaultAuthenticationEventPublisher(在SecurityAutoConfiguration中定义的)。这是一个典型的事件发布器,根据不同的认证结果发布事件,比如认证成功的话就会发布一个AuthenticationSuccessEvent事件。

public void publishAuthenticationSuccess(Authentication authentication) {
	if (applicationEventPublisher != null) {
		applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(
				authentication));
	}
}

在AuditAutoConfiguration中定义了一个AuthenticationAuditListener类型的监听器

@Bean
@ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent")
@ConditionalOnMissingBean(AbstractAuthenticationAuditListener.class)
public AuthenticationAuditListener authenticationAuditListener() throws Exception {
	return new AuthenticationAuditListener();
}

这个监听器就会接收上面的AuthenticationSuccessEvent认证成功事件然后进行相应的操作

@Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
	if (event instanceof AbstractAuthenticationFailureEvent) {
		onAuthenticationFailureEvent((AbstractAuthenticationFailureEvent) event);
	}
	else if (this.webListener != null && this.webListener.accepts(event)) {
		this.webListener.process(this, event);
	}
	else if (event instanceof AuthenticationSuccessEvent) {
		onAuthenticationSuccessEvent((AuthenticationSuccessEvent) event);
	}
}

创建认证管理器构造器,并设置上面的对象处理器(ObjectPostProcessor)、加密编码器(LazyPasswordEncoder)、认证事件发布器(AuthenticationEventPublisher)。结果如下所示
在这里插入图片描述

创建WebSecurityConfiguration Bean

对应类org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration

/**
 * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}
 * instances used to create the web configuration.
 *
 * @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
 * {@link WebSecurity} instance
 * @param webSecurityConfigurers the
 * {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
 * create the web configuration
 * @throws Exception
 */
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
		ObjectPostProcessor<Object> objectPostProcessor,
		@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
		throws Exception {
	...
}

这里首先注入对象处理器(前面已经介绍过),然后就是通过autowiredWebSecurityConfigurersIgnoreParents名称的Bean的getWebSecurityConfigurers方法获取SecurityConfigurer列表。对应的方法如下

public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
	List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
	Map<String, WebSecurityConfigurer> beansOfType = beanFactory
			.getBeansOfType(WebSecurityConfigurer.class);
	for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
		webSecurityConfigurers.add(entry.getValue());
	}
	return webSecurityConfigurers;
}

其实很简单,就是去Spring容器中获取对应类型的Bean而已。

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;

默认情况下,这里只有一个Bean。就是上面我们一直介绍的WebSecurityConfigurerAdapter,如下图所示
在这里插入图片描述

  • 创建WebSecurity对象
webSecurity = objectPostProcessor
			.postProcess(new WebSecurity(objectPostProcessor));
	if (debugEnabled != null) {
		webSecurity.debug(debugEnabled);
	}

在这里插入图片描述
对应类的注释为The WebSecurity is created by WebSecurityConfiguration to create the FilterChainProxy known as the Spring Security Filter Chain (springSecurityFilterChain). The springSecurityFilterChain is the Filter that the DelegatingFilterProxy delegates to. Customizations to the WebSecurity can be made by creating a WebSecurityConfigurer or more likely by overriding WebSecurityConfigurerAdapter..从注释来看,这个类的目的是为了创建Spring Security的拦截器链。创建完之后,通过objectPostProcessor来进行处理,这个类前面已经介绍过,主要用于自定义对象在Spring容器中作为Bean的初始化操作。仔细看下这个类,并没有自动注入,但是实现了ApplicationContextAware接口,所以在postProcess的过程中会调用以下方法

@Override
public void setApplicationContext(ApplicationContext applicationContext)
		throws BeansException {
	this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
	try {
		this.defaultWebSecurityExpressionHandler.setPermissionEvaluator(applicationContext.getBean(
				PermissionEvaluator.class));
	} catch(NoSuchBeanDefinitionException e) {}

	this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
	try {
		this.httpFirewall = applicationContext.getBean(HttpFirewall.class);
	} catch(NoSuchBeanDefinitionException e) {}
}

这里PermissionEvaluator默认实现为DenyAllPermissionEvaluator,而HttpFirewall默认没有实现。处理之后的对象内容如下
在这里插入图片描述
接下来会设置webSecurity的日志级别。默认情况下debugEnabled的值为空,这个值可以通过在@EnableWebSecurity注解中设置debug属性来设置。比如@EnableWebSecurity(debug = true)。一旦开启之后,会看到控制台打印如下的日志

2021-07-31 12:37:11.614  INFO 8816 --- [  restartedMain] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@45979052, org.springframework.security.web.context.SecurityContextPersistenceFilter@44943505, org.springframework.security.web.header.HeaderWriterFilter@564f8878, org.springframework.security.web.csrf.CsrfFilter@191ef2a2, org.springframework.security.web.authentication.logout.LogoutFilter@1b929ed3, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@32b7064d, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@7786cc4, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@52662244, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@48ccdc3b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7a78b549, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@15fd4bbb, org.springframework.security.web.session.SessionManagementFilter@227641d7, org.springframework.security.web.access.ExceptionTranslationFilter@3b526bdf, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@53688ebb]
2021-07-31 12:37:11.616  WARN 8816 --- [  restartedMain] o.s.s.c.a.web.builders.WebSecurity       : 

********************************************************************
**********        Security debugging is enabled.       *************
**********    This may include sensitive information.  *************
**********      Do not use in a production system!     *************
********************************************************************

在业务操作的时候日志如下

2021-07-31 12:39:16.284  INFO 8816 --- [nio-8080-exec-3] Spring Security Debugger                 : 

************************************************************

Request received for POST '/login':

org.apache.catalina.connector.RequestFacade@66544499

servletPath:/login
pathInfo:null
headers: 
host: localhost:8080
connection: keep-alive
content-length: 73
cache-control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
upgrade-insecure-requests: 1
origin: http://localhost:8080
content-type: application/x-www-form-urlencoded
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: same-origin
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
referer: http://localhost:8080/login
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cookie: XSRF-TOKEN=501b6461-5bf8-4b79-8594-f1bcff6cdf10; JSESSIONID=6BEB8AAEC40B369CE00E229A618E335A


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  CsrfFilter
  LogoutFilter
  UsernamePasswordAuthenticationFilter
  DefaultLoginPageGeneratingFilter
  DefaultLogoutPageGeneratingFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************

因此在生产情况下是不建议打开的,这涉及到很多私密信息。
在设置完日志功能之后,就针对注入的安全配置器列表进行排序,然后引用到web安全中。

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;

对应的apply方法是在抽象类AbstractConfiguredSecurityBuilder中定义的,这里采用模板方法模式将一个SecurityConfigurer配置到SecurityBuilder。

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

private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
	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");
		}
		List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
				.get(clazz) : null;
		if (configs == null) {
			configs = new ArrayList<SecurityConfigurer<O, B>>(1);
		}
		configs.add(configurer);
		this.configurers.put(clazz, configs);
		if (buildState.isInitializing()) {
			this.configurersAddedInInitializing.add(configurer);
		}
	}
}

最后将安全配置类对象存放到属性为configurers的LinkedHashMap当中。
在这里插入图片描述

  • 创建springSecurityFilterChain Bean
// org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
/**
 * 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 = webSecurityConfigurers != null
			&& !webSecurityConfigurers.isEmpty();
	if (!hasConfigurers) {
		WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
				.postProcess(new WebSecurityConfigurerAdapter() {
				});
		webSecurity.apply(adapter);
	}
	return webSecurity.build();
}

这里根据是否有配置WebSecurityConfigurer(hasConfigurers),在WebSecurityConfiguration创建的时候注入的。如果用户配置了,则直接调用webSecurity的构造方法,用户没定义的话,则使用一个默认的WebSecurityConfigurerAdapter实现(不覆盖任何方法)。然后添加到webSecurity当中再调用build。

注:这里使用到了webSecurity属性,那么如何保证这个属性已经初始化了呢?这个属性是定义在WebSecurityConfiguration类当中,而springSecurityFilterChain也是这个类中定义的Bean,对应的方法是实例方法,所以创建springSecurityFilterChain这个Bean之前必须先完成WebSecurityConfiguration类型Bean的创建,这其中就自然包含了属性的自动注入,而webSecurity就是在setFilterChainProxySecurityConfigurer方法中初始化的,这个方法就添加了@Autowired注解。所以webSecurity的初始化一定是在springSecurityFilterChain实例化之前的。那么如果将springSecurityFilterChain方法改为静态方法是否可以呢?这个交给读者去思考了!

通过build最终构造用于Spring Security的拦截器链。也就是这里名称为springSecurityFilterChain的Bean,本质是一个Filter。build方法是在AbstractSecurityBuilder中定义的

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

protected abstract O doBuild() throws Exception;

这里处理并发安全的问题,而且保证只构造一个过滤器。真实构造的实现在子类AbstractConfiguredSecurityBuilder中实现。

/**
 * Executes the build using the {@link SecurityConfigurer}'s that have been applied
 * using the following steps:
 *
 * <ul>
 * <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>
 * <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any
 * {@link SecurityConfigurer} that was applied to this builder.</li>
 * <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>
 * <li>Invokes {@link #performBuild()} which actually builds the Object</li>
 * </ul>
 */
@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;
	}
}

这是典型的模板方法模式,取决于子类的实现,在这里是WebSecurity类的实现。beforeInit没有实现。

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

这里会调用SecurityConfigurer的初始化方法。也就是WebSecurityConfigurerAdapter#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方法获取HttpSecurity对象。也是用户配置安全策略的地方。

/**
 * Creates the {@link HttpSecurity} or returns the current instance
 *
 * ] * @return the {@link HttpSecurity}
 * @throws Exception
 */
@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协议安全
	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;
}

首先构造一个默认的认证构造器

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

如果用户没有覆盖configure方法,则disableLocalConfigureAuthenticationBldr被设置为true,然后会通过authenticationConfiguration获取认证管理器。

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		this.disableLocalConfigureAuthenticationBldr = true;
	}

如果用户覆盖了这个方法,比如

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("admin")
            .password(passwordEncoder().encode("123456"))
            .authorities("ADMIN");

在这里设置了一个内存认证,其实就是在认证管理器构造器中配置一个InMemoryUserDetailsManagerConfigurer对象。
在这里插入图片描述
配置完成后就调用认证管理器构造器的build方法返回一个认证管理器。

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

在这里插入图片描述
仔细分析一些WebSecurity、HttpSercurity、AuthenticationManagerBuilder的继承结构,这些类都是继承自AbstractConfiguredSecurityBuilder,所以build方法都是一样的逻辑。都是通过SecurityConfigurer配置安全策略。在这里就是InMemoryUserDetailsManagerConfigurer。
这里比较重要的就是org.springframework.security.config.annotation.SecurityConfigurer#configure方法

@Override
public void configure(B builder) throws Exception {
	initUserDetailsService();

	super.configure(builder);
}

初始化UserDetailsService信息,用于获取认证用户信息。这类的userBuilders就是之前设置的。通过这个userBuilder就可以创建用户。

protected void initUserDetailsService() throws Exception {
	for (UserDetailsBuilder userBuilder : userBuilders) {
		getUserDetailsService().createUser(userBuilder.build());
	}
	for (UserDetails userDetails : this.users) {
		getUserDetailsService().createUser(userDetails);
	}
}

在这里插入图片描述
接下来在performBuild方法中创建ProviderManager实例

@Override
protected ProviderManager performBuild() throws Exception {
	if (!isConfigured()) {
		logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
		return null;
	}
	ProviderManager providerManager = new ProviderManager(authenticationProviders,
			parentAuthenticationManager);
	if (eraseCredentials != null) {
		providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
	}
	if (eventPublisher != null) {
		providerManager.setAuthenticationEventPublisher(eventPublisher);
	}
	providerManager = postProcess(providerManager);
	return providerManager;
}

在这里插入图片描述
其实这个ProviderManager也是一个认证管理器。这个类的最关键属性是providers,用于存放AuthenticationProvider的列表,在认证的时候会通过遍历这个列表查找可使用的认证方式。如果有一个满足的话,则返回对应的认证信息。比如DaoAuthenticationProvider就是比较常用的,比如以下就是当前创建ProviderManager传入的。这个正是在前面initUserDetailsService过程中产生的。
在这里插入图片描述
构造了providerManager实例之后,会设置事件处理器,Spring初始化操作,主要是做一些检查操作和设置MessageSource 。

public void afterPropertiesSet() throws Exception {
	checkState();
}

private void checkState() {
	if (parent == null && providers.isEmpty()) {
		throw new IllegalArgumentException(
				"A parent AuthenticationManager or a list "
						+ "of AuthenticationProviders is required");
	}
}

public void setMessageSource(MessageSource messageSource) {
	this.messages = new MessageSourceAccessor(messageSource);
}

最后完整信息如下所示
在这里插入图片描述
在getHttp方法当中authenticationManager()构造的认证管理器其实就是ProviderManager实例。然后再根据以上的认证管理器再创建另一个认证管理器,并设置为parent,共享全局的一些bean,创建HttpSecurity实例。

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

http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
		sharedObjects);

在这里插入图片描述
设置各种默认策略并通过configure对http进行配置(configure是用户进行配置的一个扩展点)。

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

其实上面的配置操作就是添加SecurityConfigurer实例到configurers列表中。比如通过调用securityContext()方法添加一个SecurityContextConfigurer.接下来还会通过SpringFactoriesLoader加载spring.factories中配置的AbstractHttpConfigurer实现列表,默认情况下spring.factories当中是没有的,用户可以通过这种方式配置。
在这里插入图片描述
再最后返回HttpSecurity对象之前再进行一些配置。对应的方法为

protected void configure(HttpSecurity http) throws Exception {
	logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

	http
		.authorizeRequests()
			.anyRequest().authenticated()
			.and()
		.formLogin().and()
		.httpBasic();
}

可以看到这个方法是protected级别的,用户可以按照自己的逻辑覆盖

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .logout()
            .and()
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}

最后设置如下
在这里插入图片描述
创建完HttpSecurity之后,就加入WebSecurity的securityFilterChainBuilders属性当中。并添加一个异步操作。这个异步操作用于获取FilterSecurityInterceptor,然后设置到WebSecurity当中(这个异步动作在创建过滤器完成之前再调用的)。
进入performBuild操作,这也是最终生成过滤器的地方

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

这里的逻辑也不是很难,通过ignoredRequests和securityFilterChainBuilders,来构造SecurityFilterChain列表。比如前面创建的HttpSecurity实例就存放在securityFilterChainBuilders列表中。通过HttpSecurity的build方法返回SecurityFilterChain加入列表。最后再将SecurityFilterChain列表包装到FilterChainProxy对象中。HttpSecurity同样继承自AbstractConfiguredSecurityBuilder,build方法也是要遍历其中的configurers进行初始化。
比如FormLoginConfigurer的初始化

@Override
public void init(H http) throws Exception {
	super.init(http);
	initDefaultLoginFilter(http);
}

父类初始化AbstractAuthenticationFilterConfigurer#init

@Override
public void init(B http) throws Exception {
    // 更新默认的认证
	updateAuthenticationDefaults();
	updateAccessDefaults(http);
	registerDefaultAuthenticationEntryPoint(http);
}
  • 更新默认认证权限

设置默认的登录页面和登录错误访问页面,分别为/login和/login?error

protected final void updateAuthenticationDefaults() {
	if (loginProcessingUrl == null) {
		// 设置需要认证验证的URL 在这里是login
		loginProcessingUrl(loginPage);
	}
	if (failureHandler == null) {
		// 设置登录错误处理页面 /login?error
		failureUrl(loginPage + "?error");
	}

	final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(
			LogoutConfigurer.class);
	if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
		logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");
	}
}

/**
 * Specifies the URL to validate the credentials.
 *
 * @param loginProcessingUrl the URL to validate username and password
 * @return the {@link FormLoginConfigurer} for additional customization
 */
public T loginProcessingUrl(String loginProcessingUrl) {
	this.loginProcessingUrl = loginProcessingUrl;
	// 设置RequestMatcher用于匹配HttpServletRequest
	authFilter
			.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
	return getSelf();
}

  • 更新默认访问控制
/**
 * Updates the default values for access.
 */
protected final void updateAccessDefaults(B http) {
	if (permitAll) {
		PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
	}
}
  • 注册默认认证进入端口
protected final void registerDefaultAuthenticationEntryPoint(B http) {
	registerAuthenticationEntryPoint(http, this.authenticationEntryPoint);
}

protected final void registerAuthenticationEntryPoint(B http, AuthenticationEntryPoint authenticationEntryPoint) {
	ExceptionHandlingConfigurer<B> exceptionHandling = http
			.getConfigurer(ExceptionHandlingConfigurer.class);
	if (exceptionHandling == null) {
		return;
	}
	exceptionHandling.defaultAuthenticationEntryPointFor(
			postProcess(authenticationEntryPoint), getAuthenticationEntryPointMatcher(http));
}
  • 初始化默认的登录过滤器

主要是设置loginPageGeneratingFilter的登录用户名参数名、密码参数名、登录页面、认证失败访问页面等

private void initDefaultLoginFilter(H http) {
	DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
			.getSharedObject(DefaultLoginPageGeneratingFilter.class);
	if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {
		loginPageGeneratingFilter.setFormLoginEnabled(true);
		loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());
		loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());
		loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
		loginPageGeneratingFilter.setFailureUrl(getFailureUrl());
		loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
	}
}

在这里插入图片描述
执行完所有配置的init方法之后,最主要的方法就是configure,这里也是各种过滤器生成的地方,最后在performBuild中构造SecurityFilterChain的默认实现DefaultSecurityFilterChain的实例。
在这里插入图片描述
如上图所示,通过前面configure方法配置的过滤器一共有14个。首先进行排序,这里的排序是一开始就定义好的,
在这里插入图片描述
创建FilterChainProxy实例,并根据是否需要打印日志进行装饰,最后生成的过滤器链代理类如下所示
在这里插入图片描述
最后,整个Web容器包含的过滤器如下所示,而其中的springSecurityFilterChain就是用于实现Spring Security的功能的,上面那么多步骤最终目的也是为了构造这个过滤器而已、
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值