SpringSecurity源码-HttpSecurity构建SecurityFilterChain

发布时间 2023-04-12 19:56:10作者: shigp1

简介

WebSecurity调用performBuild构建FilterChainProxy时会调用securityFilterChainBuilders集合里的每个元素的securityFilterChainBuilder.build()创建SecurityFilterChain。securityFilterChainBuilders默认只有HttpSecurity一个元素。
 

HttpSecurity继承了AbstractConfiguredSecurityBuilder,调用HttpSecurity#build会执行到AbstractConfiguredSecurityBuilder#doBuild()构建生命周期。

执行init方法

Configurer类的init方法是进行初始化工作。HttpSecurity里的configurers字段默认有10个Configurer类。这10个Configurer类可以看WebSecurityConfigurerAdapter#applyDefaultConfiguration。10个Configurer类分别是CsrfConfigurer,ExceptionHandlingConfigurer,HeadersConfigurer,SessionManagementConfigurer,SecurityContextConfigurer,RequestCacheConfigurer,AnonymousConfigurer,ServletApiConfigurer,DefaultLoginPageConfigurer,LogoutConfigurer。

 
我们自定义Security配置时一般会重写WebSecurityConfigurerAdapter#configure(HttpSecurity http):

http.authorizeRequests()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .permitAll();

authorizeRequests会添加ExpressionUrlAuthorizationConfigurer配置,formLogin添加FormLoginConfigurer。加上这两个一共12个Configurer。

 
 
SessionManagementConfigurer#init

	@Override
public void init(H http) {
	SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
	boolean stateless = isStateless();
	if (securityContextRepository == null) {
		if (stateless) {
			http.setSharedObject(SecurityContextRepository.class, new NullSecurityContextRepository());
		}
		else {
			HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
			httpSecurityRepository.setDisableUrlRewriting(!this.enableSessionUrlRewriting);
			httpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());
			AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
			if (trustResolver != null) {
				httpSecurityRepository.setTrustResolver(trustResolver);
			}
			http.setSharedObject(SecurityContextRepository.class, httpSecurityRepository);
		}
	}
	RequestCache requestCache = http.getSharedObject(RequestCache.class);
	if (requestCache == null) {
		if (stateless) {
			http.setSharedObject(RequestCache.class, new NullRequestCache());
		}
	}
	http.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy(http));
	http.setSharedObject(InvalidSessionStrategy.class, getInvalidSessionStrategy());
}

SessionManagementConfigurer是对session进行管理的。SecurityContextRepository默认是null,stateless默认是false,SecurityContextRepository设置为HttpSessionSecurityContextRepository对象并添加到SharedObject中。SessionAuthenticationStrategy设置为CompositeSessionAuthenticationStrategy,CompositeSessionAuthenticationStrategy对session策略进行代理。里面默认为ChangeSessionIdAuthenticationStrategy。并CompositeSessionAuthenticationStrategy将添加到SharedObject中。设置InvalidSessionStrategy为null并将InvalidSessionStrategy添加到SharedObject中。SessionManagementConfigurer#init主要创建了session的一些属性。
 
RequestCacheConfigurer#init

public void init(H http) {
	http.setSharedObject(RequestCache.class, getRequestCache(http));
}

初始化RequestCache为HttpSessionRequestCache并添加到SharedObject中。

 

AnonymousConfigurer#init

public void init(H http) {
	if (this.authenticationProvider == null) {
		this.authenticationProvider = new AnonymousAuthenticationProvider(getKey());
	}
	if (this.authenticationFilter == null) {
		this.authenticationFilter = new AnonymousAuthenticationFilter(getKey(), this.principal, this.authorities);
	}
	this.authenticationProvider = postProcess(this.authenticationProvider);
	http.authenticationProvider(this.authenticationProvider);
}

AnonymousConfigurer是配置匿名用户登陆的。进行一些属性的初始化工作。AuthenticationProvider设置为AnonymousAuthenticationProvider。AnonymousAuthenticationFilter设置为AnonymousAuthenticationFilter。将AuthenticationProvider添加到HttpSecurity中。

 

DefaultLoginPageConfigurer#init

public void init(H http) {
	this.loginPageGeneratingFilter.setResolveHiddenInputs(DefaultLoginPageConfigurer.this::hiddenInputs);
	this.logoutPageGeneratingFilter.setResolveHiddenInputs(DefaultLoginPageConfigurer.this::hiddenInputs);
	http.setSharedObject(DefaultLoginPageGeneratingFilter.class, this.loginPageGeneratingFilter);
}

DefaultLoginPageConfigurer是配置默认登录页的。配置了DefaultLoginPageGeneratingFilter并将DefaultLoginPageGeneratingFilter加入到SharedObject中。

 

LogoutConfigurer#init

public void init(H http) {
	if (this.permitAll) {
		PermitAllSupport.permitAll(http, this.logoutSuccessUrl);
		PermitAllSupport.permitAll(http, this.getLogoutRequestMatcher(http));
	}
	DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
			.getSharedObject(DefaultLoginPageGeneratingFilter.class);
	if (loginPageGeneratingFilter != null && !isCustomLogoutSuccess()) {
		loginPageGeneratingFilter.setLogoutSuccessUrl(getLogoutSuccessUrl());
	}
}

LogoutConfigurer是配置退出登陆的。配置DefaultLoginPageGeneratingFilter的logoutSuccessUrl属性。

 

FormLoginConfigurer#init

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

FormLoginConfigurer是配置登录的。默认配置了登录url为/login,登录出错url是/login?error,用户名参数是username,密码参数是password,这两个参数是从HttpRequest获取用户名和密码的。

 

其余Configurer类的init方法是空的,略过。

执行configure方法

CsrfConfigurer#configure

public void configure(H http) {
	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(getSessionAuthenticationStrategy());
	}
	filter = postProcess(filter);
	http.addFilter(filter);
}

是对CsrfFilter做一些配置。csrfTokenRepository是LazyCsrfTokenRepository,LazyCsrfTokenRepository对HttpSessionCsrfTokenRepository进行了代理,HttpSessionCsrfTokenRepository将CSRF-TOKEN存到session中或者生成CSRF-TOKEN。LazyCsrfTokenRepository包装了HttpSessionCsrfTokenRepository,延迟了生成和保存CSRF-TOKEN,在需要时才生成和保存CSRF-TOKEN。AccessDeniedHandler是对
CSRF-TOKEN无效或过期时进行处理。LogoutConfigurer添加CsrfLogoutHandler用于退出登录时清除CSRF-TOKEN。getSessionAuthenticationStrategy()创建CsrfAuthenticationStrategy用于身份认证时改变或移除CSRF-TOKEN。并将CsrfAuthenticationStrategy加入SessionManagementConfigurer中。

 

ExceptionHandlingConfigurer#configure

public void configure(H http) {
	AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
	ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,
			getRequestCache(http));
	AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
	exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
	exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
	http.addFilter(exceptionTranslationFilter);
}

entryPoint默认是LoginUrlAuthenticationEntryPoint,创建ExceptionTranslationFilter,deniedHandler用于处理权限异常AccessDeniedException。

 

HeadersConfigurer#configure

public void configure(H http) {
	HeaderWriterFilter headersFilter = createHeaderWriterFilter();
	http.addFilter(headersFilter);
}

HeaderWriterFilter设置了往响应中要添加的响应头。

 

SessionManagementConfigurer#configure

public void configure(H http) {
	SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
	SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository,
			getSessionAuthenticationStrategy(http));
	if (this.sessionAuthenticationErrorUrl != null) {
		sessionManagementFilter.setAuthenticationFailureHandler(
				new SimpleUrlAuthenticationFailureHandler(this.sessionAuthenticationErrorUrl));
	}
	InvalidSessionStrategy strategy = getInvalidSessionStrategy();
	if (strategy != null) {
		sessionManagementFilter.setInvalidSessionStrategy(strategy);
	}
	AuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler();
	if (failureHandler != null) {
		sessionManagementFilter.setAuthenticationFailureHandler(failureHandler);
	}
	AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
	if (trustResolver != null) {
		sessionManagementFilter.setTrustResolver(trustResolver);
	}
	sessionManagementFilter = postProcess(sessionManagementFilter);
	http.addFilter(sessionManagementFilter);
	if (isConcurrentSessionControlEnabled()) {
		ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http);

		concurrentSessionFilter = postProcess(concurrentSessionFilter);
		http.addFilter(concurrentSessionFilter);
	}
	if (!this.enableSessionUrlRewriting) {
		http.addFilter(new DisableEncodeUrlFilter());
	}
	if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
		http.addFilter(new ForceEagerSessionCreationFilter());
	}
}

SecurityContextRepository用于在认证成功的每个请求设置SecurityContext,默认是HttpSessionSecurityContextRepository,从session中加载SecurityContext并设置到请求中。SessionAuthenticationStrategy默认是CompositeSessionAuthenticationStrategy,SessionAuthenticationStrategy主要是用于在非匿名身份认证时,可以自定义策略去对HttpSession进行相关操作。
典型用途是确保会话存在或更改会话ID以防止会话固定攻击ChangeSessionIdAuthenticationStrategy和SessionFixationProtectionStrategy。设置了session失效策略,身份认证失败时的handle。设置了用于处理session并发的ConcurrentSessionFilter。DisableEncodeUrlFilter用于禁止将sessionId链接到url后面。ForceEagerSessionCreationFilter用于早期创建session。

 
SecurityContextConfigurer#configure

public void configure(H http) {
	SecurityContextRepository securityContextRepository = getSecurityContextRepository();
	if (this.requireExplicitSave) {
		SecurityContextHolderFilter securityContextHolderFilter = postProcess(
				new SecurityContextHolderFilter(securityContextRepository));
		http.addFilter(securityContextHolderFilter);
	}
	else {
		SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
				securityContextRepository);
		SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
		SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
				? sessionManagement.getSessionCreationPolicy() : null;
		if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
			securityContextFilter.setForceEagerSessionCreation(true);
			http.addFilter(postProcess(new ForceEagerSessionCreationFilter()));
		}
		securityContextFilter = postProcess(securityContextFilter);
		http.addFilter(securityContextFilter);
	}
}

配置SecurityContextPersistenceFilter过滤器对每个请求用SecurityContextRepository加载SecurityContextHolder或清除SecurityContextHolder。
 
RequestCacheConfigurer#configure

public void configure(H http) {
	RequestCache requestCache = getRequestCache(http);
	RequestCacheAwareFilter requestCacheFilter = new RequestCacheAwareFilter(requestCache);
	requestCacheFilter = postProcess(requestCacheFilter);
	http.addFilter(requestCacheFilter);
}

创建RequestCacheAwareFilter。

 

AnonymousConfigurer#configure

public void configure(H http) {
	this.authenticationFilter.afterPropertiesSet();
	http.addFilter(this.authenticationFilter);
}

校验AnonymousAuthenticationFilter的一些属性。

 

ServletApiConfigurer#configure

public void configure(H http) {
	this.securityContextRequestFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
	ExceptionHandlingConfigurer<H> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
	AuthenticationEntryPoint authenticationEntryPoint = (exceptionConf != null)
			? exceptionConf.getAuthenticationEntryPoint(http) : null;
	this.securityContextRequestFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
	LogoutConfigurer<H> logoutConf = http.getConfigurer(LogoutConfigurer.class);
	List<LogoutHandler> logoutHandlers = (logoutConf != null) ? logoutConf.getLogoutHandlers() : null;
	this.securityContextRequestFilter.setLogoutHandlers(logoutHandlers);
	AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
	if (trustResolver != null) {
		this.securityContextRequestFilter.setTrustResolver(trustResolver);
	}
	ApplicationContext context = http.getSharedObject(ApplicationContext.class);
	if (context != null) {
		String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
		if (grantedAuthorityDefaultsBeanNames.length == 1) {
			GrantedAuthorityDefaults grantedAuthorityDefaults = context
					.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
			this.securityContextRequestFilter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
		}
	}
	this.securityContextRequestFilter = postProcess(this.securityContextRequestFilter);
	http.addFilter(this.securityContextRequestFilter);
}

配置SecurityContextHolderAwareRequestFilter。设置authenticationEntryPoint,logoutHandlers。

 

DefaultLoginPageConfigurer#configure

public void configure(H http) {
	AuthenticationEntryPoint authenticationEntryPoint = null;
	ExceptionHandlingConfigurer<?> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
	if (exceptionConf != null) {
		authenticationEntryPoint = exceptionConf.getAuthenticationEntryPoint();
	}
	if (this.loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {
		this.loginPageGeneratingFilter = postProcess(this.loginPageGeneratingFilter);
		http.addFilter(this.loginPageGeneratingFilter);
		LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
		if (logoutConfigurer != null) {
			http.addFilter(this.logoutPageGeneratingFilter);
		}
	}
}

将loginPageGeneratingFilter(DefaultLoginPageGeneratingFilter)加入到HttpSecurity中,同时将logoutPageGeneratingFilter(DefaultLogoutPageGeneratingFilter)加入到HttpSecurity。

 

LogoutConfigurer#configure

public void configure(H http) throws Exception {
	LogoutFilter logoutFilter = createLogoutFilter(http);
	http.addFilter(logoutFilter);
}

创建LogoutFilter,设置退出登录处理器logoutHandlers。

 
ExpressionUrlAuthorizationConfigurer#configure

public void configure(H http) throws Exception {
	FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
	if (metadataSource == null) {
		return;
	}
	FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource,
			http.getSharedObject(AuthenticationManager.class));
	if (this.filterSecurityInterceptorOncePerRequest != null) {
		securityInterceptor.setObserveOncePerRequest(this.filterSecurityInterceptorOncePerRequest);
	}
	securityInterceptor = postProcess(securityInterceptor);
	http.addFilter(securityInterceptor);
	http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}

创建FilterSecurityInterceptor。用于对HTTP资源执行安全处理。

 

FormLoginConfigurer#configure

public void configure(B http) throws Exception {
	PortMapper portMapper = http.getSharedObject(PortMapper.class);
	if (portMapper != null) {
		this.authenticationEntryPoint.setPortMapper(portMapper);
	}
	RequestCache requestCache = http.getSharedObject(RequestCache.class);
	if (requestCache != null) {
		this.defaultSuccessHandler.setRequestCache(requestCache);
	}
	this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
	this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
	this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
	if (this.authenticationDetailsSource != null) {
		this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
	}
	SessionAuthenticationStrategy sessionAuthenticationStrategy = http
			.getSharedObject(SessionAuthenticationStrategy.class);
	if (sessionAuthenticationStrategy != null) {
		this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
	}
	RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
	if (rememberMeServices != null) {
		this.authFilter.setRememberMeServices(rememberMeServices);
	}
	SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
	if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
		SecurityContextRepository securityContextRepository = securityContextConfigurer
				.getSecurityContextRepository();
		this.authFilter.setSecurityContextRepository(securityContextRepository);
	}
	F filter = postProcess(this.authFilter);
	http.addFilter(filter);
}

配置UsernamePasswordAuthenticationFilter。设置AuthenticationManager,登录成功处理器successHandler,设置登录失败处理器failureHandler,设置RememberMeServices用于RememberMe登录,设置SecurityContextRepository用于登录成功时保存 SecurityContext 。

执行HttpSecurity.performBuild

protected DefaultSecurityFilterChain performBuild() {
	ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
			ExpressionUrlAuthorizationConfigurer.class);
	AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
	boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
	Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
			"authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");
	this.filters.sort(OrderComparator.INSTANCE);
	List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
	for (Filter filter : this.filters) {
		sortedFilters.add(((OrderedFilter) filter).filter);
	}
	return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}

将Filter排序后,将全部Filter加入到DefaultSecurityFilterChain中并返回。