简介
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中并返回。
- SecurityFilterChain SpringSecurity HttpSecurity 源码securityfilterchain springsecurity httpsecurity springsecurity httpsecurity websecurity springsecurity源码 逻辑 springsecurity springboot废话 源码 securityfilterchain httpsecurity httpsecurity websecurity pig4cloud框架 springsecurity springsecurity流程 springsecurity问题