由于SpringBoot 对 Security 的支持类均位于org.springframework.boot.autoconfigure.security包下,主要通过 SecurityAutoConfiguration 自动配置类和 SecurityProperties 属性配置来完成,所以需要下载springboot源码深入学习
SecurityAutoConfiguration
首先来看SecurityAutoConfiguration做了什么事情
package org.springframework.boot.autoconfigure.security.servlet;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class, ErrorPageSecurityFilterConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
该注解的作用是当项目中存在某个类时才会使标有该注解的类或方法生效,此处表示,当前类被创建前,DefaultAuthenticationEventPublisher必须已经存在,本类中向spring容器中注入了一个DefaultAuthenticationEventPublisher类型的对象,内部实现就是spring的ApplicationEventPublisher,用于springsecurity各种权限时间的交互,如登陆失败,会发布一个事件,然后通知其它组件做出相应的响应
@EnableConfigurationProperties(SecurityProperties.class)
该注解使ConfigurationProperties注解生效,此处就是使SecurityProperties类上的ConfigurationProperties,导入配置类SecurityProperties,内部声明了user对象
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,SecurityDataConfiguration.class, ErrorPageSecurityFilterConfiguration.class })
导入四个配置类,主要看前两个
SpringBootWebSecurityConfiguration
用于注册默认的SecurityFilterChain,conditional注解前溯会在DefaultWebSecurityCondition中判断SecurityFilterChain和WebSecurityConfigurerAdapter两个都缺失或者HttpSecurity和SecurityFilterChain同时存在才会注册默认的SecurityFilterChain,由此我们可以通过编写一个WebSecurityConfigurerAdapter的继承类来自定义SpringSecurity。也就是说,继承了WebSecurityConfigurerAdapter就会创建SecurityFilterChain
WebSecurityEnablerConfiguration
WebSecurityEnablerConfiguration类上添加了@EnableWebSecurity注解,该注解上又通过@Import导入了WebSecurityConfiguration、SpringWebMvcImportSelector、OAuth2ImportSelector和HttpSecurityConfiguration,同时该注解上的@EnableGlobalAuthentication类上又通过@Import注解导入了AuthenticationConfiguration类
HttpSecurityConfiguration
使用@Autowired注入了一些bean
提供Spring的生命周期支持,主要是用于创建对象并把bean注入到spring容器中。这里注入的是AutowireBeanFactoryObjectPostProcessor类的一个实例
@Autowired
void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
鉴权对象,这里注入的是ProviderManager的一个实例(有可能在该类初始化的时候容器中还不存在ProviderManager)
void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
用于构建ProviderManager的一个配置类
@Autowired
void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
依赖注入一个AuthenticationConfiguration,该类创建了三个重要的Bean对象AuthenticationManagerBuild、InitializeUserDetailsBeanManagerConfigurer和InitializeAuthenticationProviderBeanManagerConfigurer,分别用来
- 构建AuthenticationManager
- 用来初始化spring.factories中的UserDetailsServiceAutoConfiguration,它的功能是提供默认的用户名和密码、
- 初始化用户认证管理器(ProviderManager)
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype") // 每次获得bean都会生成一个新的对象
HttpSecurity httpSecurity() throws Exception {
// 密码编码器
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
this.context);
// AuthenticationManager建造器
AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
// 获取AuthenticationManager(ProviderManager),设置为父AuthenticationManager,用于管理所有的AuthenticationProvider
authenticationBuilder.parentAuthenticationManager(authenticationManager());
// 创建一个HttpSecurity,相当于xml文件配置中的http名称空间
// 将provider注入HttpSecurity中
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
// @formatter:off
http
.csrf(withDefaults())
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling(withDefaults())
.headers(withDefaults())
.sessionManagement(withDefaults())
.securityContext(withDefaults())
.requestCache(withDefaults())
.anonymous(withDefaults())
.servletApi(withDefaults())
.apply(new DefaultLoginPageConfigurer<>());
http.logout(withDefaults());
// @formatter:on
return http;
}
WebSecurityConfiguration
WebSecurityConfiguration用来创建FilterChianProxy,它是Spring Security使用的核心。FilterChainProxy可以在客户端请求时用来确定SecurityFilterChain应该使用哪个
主要方法:
setFilterChainProxySecurityConfigurer
创建 WebSecurity 建造者对象,apply() 初始配置
注解@Value(“#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}”)用来获取所有的WebSecurityConfigurer,再注入随后创建的WebSecurity
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
webSecurityConfigurers.sort(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) {
this.webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
setFilterChainProxySecurityConfigurer方法有两个参数,第一个参数 objectPostProcessor 是一个对象后置处理器,由于该方法有一个@Autowired注解,会自动查找需要注入的参数,所以objectPostProcessor参数会自动注入进来。需要注意的是,@Autowired注解的required属性为false,所以在方法参数注入的时候,有就注入,没有则忽略。required属性设置为false主要是针对第二个参数webSecurityConfigurers ,因为该参数的值是通过调用 autowiredWebSecurityConfigurersIgnoreParents 对象的 getWebSecurityConfigurers 方法获取的。 autowiredWebSecurityConfigurersIgnoreParents 对象也是在当前类中注入到 Spring 容器中的,我们来看一下它的 getWebSecurityConfigurers 方法:
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
可以看到,在 getWebSecurityConfigurers方法中主要是通过调用 beanFactory.getBeansOfType 方法来获取 Spring 容器中所有的 WebSecurityConfigurer 实例,也就是开发者自定义的各种各样继承自WebSecurityConfigurerAdapter的配置类。如果开发者没有自定义任何配置类,那么这里获取到的就是前面所讲的SpringBootWebSecurityConfiguration 类中提供的默认配置类,将获取到的所有配置类实例放入webSecurityConfigurers集合中并返回。
继续看方法体,在该方法中,首先创建一个WebSecurity实例,创建出来之后去对象后置处理器中走一圈,这样就将webSecurity对象注册到Spring容器中了
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
接下来, 根据每一个配置类的@Order注解对webSecurityConfigurers集合中的所有配置类进行排序,因为一个配置类对应一个过滤器链,当请求到来后,需要先和哪个过滤器链进行匹配,这里必然存在一个优先级问题,所以如果开发者自定义了多个配置类,则需要通过@Order注解标记 多个配置类的优先级
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
排序完成后,进入到for循环中,检查是否存在优先级相等的配置类, 如果存在,则直接抛出异常
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;
}
最后再去遍历所有的配置类,调用webSecurity.apply方法将其添加到webSecurity父类中的configurers集合中(将来遍历该集合并分别调用配置类的init、configure和performBuild方法完成配置类的初始化操作)
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
this.webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
这是setFiltetChainProxySecurityConfigurer方法的执行逻辑,该方法主要用来初始化 WebSecurity对象,同时收集到所有的自定义配置类。有了 WebSecurity对象和配置类,接下来就可以构建过滤器FilterChainProxy 了。我们来看一下 springSecurityFilterChain 方法
setFilterChains
自动注入所有的SecurityFilteChain
@Autowired(required = false)
void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
this.securityFilterChains = securityFilterChains;
}
springSecurityFilterChain
建造者 webSecurity 调用 build() 方法,开始构建 springSecurityFilterChain
build() 方法进去继续调用了 doBuild(),该方法依次调用各个配置器的 init()、configure() 方法,等配置到位后,开始执行 performBuild(),进行核心过滤器 FilterChainProxy 的创建
public Filter springSecurityFilterChain() throws Exception {
//判断是否配置了webSecurityConfigurers
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
//判断是否配置了securityFilterChains
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
Assert.state(!(hasConfigurers && hasFilterChain),
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
if (!hasConfigurers && !hasFilterChain) {
/*如果没有配置webSecurityConfigurers和securityFilterChain则创建
WebSecurityConfigurerAdapterConfigurers在setFilterChainProxySecurityConfigurer()方法里配置*/
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
//调用build()方法创建SecurityFilterChainProxy
return this.webSecurity.build();
webSecurity.build()
build点进去
WebSecurityConfigurerAdapter.init()
首先调用getHttp()创建HttpSecurity
protected final HttpSecurity getHttp() throws Exception {
if (this.http != null) {
return this.http;
}
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
if (!this.disableDefaults) {
applyDefaultConfiguration(this.http);
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
this.http.apply(configurer);
}
}
// 这里会加载自定义继承WebSecurityConfigerAdapter的配置类
configure(this.http);
return this.http;
}
然后调用WebSecurity的addSecurityFilterChainBuilder,把HttpSecurity传过去
可以看到 HttpSecurity 的实例被加到 WebSecurity 的 securityFilterChainBuilders 属性中
init方法执行完,再来看performBuild方法
@Override
protected Filter performBuild() throws Exception {
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this is done by exposing a SecurityFilterChain bean "
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
// 创建一个名为 securityFilterChain 的 List
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
// 定义一个存放RequestMatcher的集合,用于配置忽略url和认证的url,如自定义的WebSecurityConfigurerAdapter类中的configure方法中配置 // antMatchers("/user/query").hasAnyRole("ADMIN")
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
// 遍历 securityFilterChainBuilders 中的HttpSecurity ,然后调用HttpSecurity 的 build() 方法,将返回值添加到 securityFilterChain 中
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);
}
// 创建filterChainProxy
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (this.debugEnabled) {
this.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);
}
this.postBuildAction.run();
return result;
}
-
通过上面源码可以看到:
- 先创建了一个名为 securityFilterChain 的 List
- 遍历 securityFilterChainBuilders 中的元素,然后调用元素中 build() 方法,将返回值添加到 securityFilterChain 中
- 最后将 securityFilterChain 放到 FilterChainProxy 中
在上一步中 init 方法中可以知道 securityFilterChainBuilders 中的元素是 HttpSecurity,调用 HttpSecurity 的 build() 方法返回 SecurityFilterChain 实例,最后创建FilterChainProxy集合并放入SecurityFilterChain集合
至此 SecurityFilterChain 和FilterChainProxy的初始化就完成了
AuthenticationConfiguration
简单的说,这个类的作用就是用来创建ProviderManager,ProviderManager是一个AuthenticationManager实现,用于管理所有AuthenticationProvider实现的一个管理器
首先可以看到该类通过@Import注解导入了ObjectPostProcessorConfiguration
ObjectPostProcessorConfiguration
该配置类向spring容器中注入了ObjectPostProcessor的实现类AutowireBeanFactoiyObjectPostProcessor,该实现类主要用来将一个对象注册到Spring容器中去,我们在其他配置类中所见到的ObjectPostProcessor 实例其实都是这里提供的
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ObjectPostProcessorConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}
authenticationManager用于存储一个AuthenticationManager对象,构建这个对象就是当前配置类的主要功能了
private AuthenticationManager authenticationManager;
globalAuthConfigurers是存储全局配置的一个列表,三个默认的GlobalAuthenticationConfigurerAdapter实现会被注入到这个属性列表中,他们分别是:EnableGlobalAuthenticationAutowiredConfigurer(当前配置类的一个内部类)、InitializeAuthenticationProviderBeanManagerConfigurer(初始化全局的 AuthenticationProvider对象)、InitializeUserDetailsBeanManagerConfigurer(初始化全局的 AuthenticationProvider对象),这三个类都是在当前配置类中使用@Bean注解引入,然后通过@Autowired注解的setGlobalAuthenticationConfigurers()方法将他们注入到globalAuthConfigurers属性中
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
authenticationManagerBuilder()方法,这个方法创建一个DefaultPasswordEncoderAuthenticationManagerBuilder,它是AuthenticationManagerBuilder的一个实现类,这个类使用密码解码器的延时加载策略LazyPasswordEncoder,如果能从applicationContext中获取到AuthenticationEventPublisher,也会将AuthenticationEventPublisher这个事件发布器设置到AuthenticationManagerBuilder中,DefaultPasswordEncoderAuthenticationManagerBuilder作为AuthenticationManagerBuilder的实现类,最后会在getAuthenticationManager方法中被用来创建AuthenticationManager的实现类ProviderManager
@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;
}
getAuthenticationManager 方法用来构建具体的 AuthenticationManager 对象,在该方法内部,会首先判断AuthenticationManager对象是否已经初始化,如果已经初始化,则直接 返回 AuthenticationManager 对象,否则就先从 Spring 容器中获取到 AuthenticationManagerBuilder对象。注意这里还多了一个AuthenticationManagerDelegator对象,这个主要是为了防止在初始化AuthenticationManager时进行无限递归,拿到authBuilder对象之后,接下来遍历 globalAuthConfigurers配置类集合(也就是第二点中所说的三个配置类),将配置类分别添加到authBuilder对象中,然后进行构建,最终将构建结果返回
public AuthenticationManager getAuthenticationManager() throws Exception {
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
authBuilder.apply(config);
}
this.authenticationManager = authBuilder.build();
if (this.authenticationManager == null) {
this.authenticationManager = getAuthenticationManagerBean();
}
this.authenticationManagerInitialized = true;
return this.authenticationManager;
}
如果开发者在自定义配置类中重写了 configure(AuthenticationManagerBuilder)方法,这里的全局AuthenticationManager对象将不会生效,而大部分情况下,开发者都会重写 configure(AuthenticationManagerBuilder)方法,如
AuthenticationManager、ProviderManager和AuthenticationProvider三者之间的关系
以UsernamePasswordAuthenticationFilter为例,看一下三者关系
从上图可以看出验证逻辑为:
1、在UsernamePasswordAuthenticationFilter的attemptAuthentication()方法中,调用AuthenticationManager进行认证
2、AuthenticationManager接收Authentication对象作为参数,并通过authenticate方法对其进行验证(实际由其实现类ProviderManager完成)
3、在ProviderManager的authenticate方法中,轮训成员变量List providers。该providers中如果有一个
AuthenticationProvider的supports函数返回true,那么就会调用该AuthenticationProvider的authenticate函数认证,如果认证成功则整个
认证过程结束。如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证成功则为认证成功。
4、UsernamePasswordAuthenticationToken实现了Authentication,主要是将用户输入的用户名密码进行封装,并提供给
AuthenticationManager进行验证,验证成功后,返回一个认证成功的UsernamePasswordAuthenticationToken对象
总结
AuthenticationConfiguration会获取到容器中所有的GlobalAuthenticationConfigurerAdapter实现,然后创建一个默认的AuthenticationManagerBuilder(就是DefaultPasswordEncoderAuthenticationManagerBuilder),接着将所有GlobalAuthenticationConfigurerAdapter配置设置到DefaultPasswordEncoderAuthenticationManagerBuilder中,然后提供一个对外方法getAuthenticationManager(),这个方法中会获取到DefaultPasswordEncoderAuthenticationManagerBuilder,然后创建一个AuthenticationManager(就是ProviderManager),这就是AuthenticationConfiguration所做的事情,整体来说,AuthenticationConfiguration的作用主要体现在两方面:第一就是导入了 ObjectPostProcessorConfiguration 配置类;第二则是提供 了一个全局的 AuthenticationManager 对象
authenticationEventPublisher
SecurityAutoConfiguration类上的所有注解都跟完之后,再来看这个类唯一的一个方法authenticationEventPublisher,它向spring容器中注入了一个AuthenticationEventPublisher的实现类DefaultAuthenticationEventPublisher并添加了一系列的异常
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
public DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
addMapping(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);
addMapping(UsernameNotFoundException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);
addMapping(AccountExpiredException.class.getName(), AuthenticationFailureExpiredEvent.class);
addMapping(ProviderNotFoundException.class.getName(), AuthenticationFailureProviderNotFoundEvent.class);
addMapping(DisabledException.class.getName(), AuthenticationFailureDisabledEvent.class);
addMapping(LockedException.class.getName(), AuthenticationFailureLockedEvent.class);
addMapping(AuthenticationServiceException.class.getName(), AuthenticationFailureServiceExceptionEvent.class);
addMapping(CredentialsExpiredException.class.getName(), AuthenticationFailureCredentialsExpiredEvent.class);
addMapping("org.springframework.security.authentication.cas.ProxyUntrustedException",
AuthenticationFailureProxyUntrustedEvent.class);
addMapping("org.springframework.security.oauth2.server.resource.InvalidBearerTokenException",
AuthenticationFailureBadCredentialsEvent.class);
}
当我们自定义过滤器时就可以抛出这些异常
SecurityFilterAutoConfiguration
当 Spring Boot 项目启动后,SecurityFilterAutoConfiguration类会加载 DelegatingFilterProxyRegistrationBean注册过滤器,名字为 springSecurityFilterChain
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
// 加载完SecurityAutoConfiguration之后才会加载本类
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
}
}
DelegatingFilterProxyRegistrationBean
该类getFilter方法返回的是DelegatingFilterProxy
@Override
public DelegatingFilterProxy getFilter() {
return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
@Override
protected void initFilterBean() throws ServletException {
// Don't initialize filter bean on init()
}
};
}
DelegatingFilterProxy
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
这里delegateToUse是FilterChainProxy对象,由此可知,DelegatingFilterProxy类通过 springSecurityFilterChain这个名称,得到了一个 FilterChainProxy过滤器,最终执行的是这个过滤器的 doFilter方法
FilterChainProxy
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 在 doFilter 方法中,正常来说,clearContext 参数每次都是 true,于是每次都先给 request 标记上 FILTER_APPLIED 属性,然后执行
// doFilterInternal 方法去走过滤器,执行完毕后,最后在 finally 代码块中清除 SecurityContextHolder 中保存的用户信息,同时移除 request // 中的标记
// FILTER_APPLIED 变量用来标记过滤器是否已经执行过了
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (!clearContext) {
doFilterInternal(request, response, chain);
return;
}
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
}
catch (RequestRejectedException ex) {
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
跟进doFilterInternal方法
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将请求封装成一个FirewalledRequest对象,在封装的过程中也会判断请求是否合法
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
// 对响应进行封装
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
// 调用getFilters方法根据当前的请求,从filterChains中找到对应的过滤器链,然后由该过滤器链去处理请求
List<Filter> filters = getFilters(firewallRequest);
// 如果找出来的filters为null,或者集合中没有元素,那就说明当前请求不需要经过过滤器。直接执行chain.doFilter,这就又回到原生过滤器
// 中去了。如果发生这种情况,那就是针对项目中的静态资源,如果我们配置了资源放行,如web.ignoring().antMatchers("/hello");,
// 那么当你请求/hello接口时就会走到这里来,也就是说这个不经过Spring Security Filte
if (filters == null || filters.size() == 0) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
}
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
return;
}
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
}
// 如果过滤器不为空,构建虚拟的过滤器链
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
// 执行virtualFilterChain的doFilter方法
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}
跟进virtualFilterChain
virtualFilterChain
private static final class VirtualFilterChain implements FilterChain {
// 原生的过滤器链,也就是Web Filter
private final FilterChain originalChain;
// Spring Security中的过滤器链
private final List<Filter> additionalFilters;
// 当前请求
private final FirewalledRequest firewalledRequest;
// 过滤器链中过滤器的个数
private final int size;
// 过滤器链过滤时候的下标
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain,
List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// currentPosition == size表示过滤器链已经执行完毕,此时通过调用originalChain.doFilter进入到原生过滤器链中,同时也退出了 Spring // Security 过滤器链。否则就从 additionalFilters 取出 Spring Security 过滤器链中的一个个过滤器,挨个调用 doFilter 方法
if (this.currentPosition == this.size) {
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
this.originalChain.doFilter(request, response);
return;
}
this.currentPosition++;
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),
this.currentPosition, this.size));
}
nextFilter.doFilter(request, response, this);
}
}
设计模式
spring security继承了spring源码中简洁高效的编码风格,大量使用了注解和设计模式,举例:
模板方法(钩子函数、回调函数)
如AbstractUserDetailsAuthenticationProvider类中定义了两个空方法additionalAuthenticationChecks和retrieveUser,这两个方法由DaoAuthenticationProvider实现
责任链模式
这个不多说,security玩的就是责任链
装饰器模式
如上面刚说的FireWalledRequest
观察者模式
如AuthenticationEventPublisher,当出现异常时会调用该类发布事件
建造者模式
如AuthenticationManagerBuild
适配器模式
如WebSecurityConfigurerAdapter
策略模式
如果定义多个过滤器链会通过@Order进行排序,内部重写了compare方法,这就是典型的策略模式
代理模式
典型的如DelegatingFilterProxy,代理了FilterChainProxy。Spring Security Filter并不是直接嵌入到 Web Filter中的,而是通过FilterChainProxy来统一管理 Spring Security Filter,FilterChainProxy本身则通过Spring提供的DelegatingFilterProxy代理过滤器嵌入到Web Filter之中