SpringSecurity|SpringSecurity 初始化流程源码
SpringSecurity 初始化流程源码
本篇主要讲解 SpringSecurity初始化流程的源码部分,包括核心的 springSecurityFilterChain 是如何创建的,以及在介绍哪里可以扩展个性化的配置,SpringSecurity源码其实是蛮难得 各种Builder Configure 看得真的头疼!
?1.简单介绍
?SpringSecurity 的核心功能主要包括:
?认证 (你是谁)
?授权 (你能干什么)
?攻击防护 (防止伪造身份)
?其核心就是一组过滤器链,项目启动后将会自动配置,本篇也会涉及过滤器链是如何自动初始化的。
文章图片
Xnip20200118_195949.png ?SecurityContextPersistenceFilter 是最前面的一个filter
?请求到它时候会去检查 根据sessionId找到session 判断session 中是否存在 SecurityContext 在 则将 SecurityContext 存入当前的线程中去
?响应的时候,看当前线程是否有SecurityContext ,如果有 放入到session中去 这样不同的请求都能拿到相同的 用户认证信息。
?UsernamePasswordAuthenticationFilter 该过滤器是处理表单登录的,通过表单登录提交的认证都会经过它处理
?SocialAuthenticationFilter 比如这个就是社交登录使用的Filter
?详细可以看我另外一篇 SpringSocial 实现第三方QQ登录SpringSocial 实现第三方QQ登录
?绿色的过滤器都是可配置的,其他颜色的都不行!
?2.SecurityAutoConfiguration
?如果是SpringBoot项目只要你依赖了SpringSecurity相关依赖依然会有自动配置类
SecurityAutoConfiguration 生效 它会导入 WebSecurityEnableConfiguration
文章图片
Xnip20200118_201432.png ?@EnableWebSecurity将会是我们本篇的主要切入点
文章图片
Xnip20200118_201547.png ?3.@EnableWebSecurity注解介绍
?该注解 它是初始化Spring Security的入口 .
?打开@EnableWebSecurity注解
@Retention(value = https://www.it610.com/article/java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}
?该注解类通过@Configuration和@Import配合使用引入了一个配置类(WebSecurityConfiguration)和两个ImportSelector(SpringWebMvcImportSelector,OAuth2ImportSelector),我们重点关注下WebSecurityConfiguration,它是Spring Security的核心
?4.springSecurityFilterChain初始化流程及源码
?打开WebSecurityConfiguration 它是一个配置类,主要看 springSecurityFilterChain()方法,它就是初始化
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();
}
?@Bean注解name属性值AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME就是XML中定义的springSecurityFilterChain
?从源码中知道过滤器通过最后的 webSecurity.build()创建,webSecurity的类型为:WebSecurity,它在 setFilterChainProxySecurityConfigurer方法中优先被创建了:
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor
?从代码中可以看到,它是直接被new出来的:
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
【SpringSecurity|SpringSecurity 初始化流程源码】?setFilterChainProxySecurityConfigurer 该方法的webSecurityConfigurers 参数是通过@Value注入的
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
?AutowiredWebSecurityConfigurersIgnoreParents的 getWebSecurityConfigurers()
?如下,就是获取所有的 WebSecurityConfigurer的类型的配置类
?而通常 我们通过继承 WebSecurityConfigurerAdapter 来自定义WebSecurityConfigurer
public List> getWebSecurityConfigurers() {
List> webSecurityConfigurers = new ArrayList<>();
Map beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
?再回到setFilterChainProxySecurityConfigurer方法 下面有一段这样的代码 ,对于上面获取的所有的WebSecurityConfigurer类型 循环执行 webSecurity的apply方法
for (SecurityConfigurer webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
?webSecurity集成AbstractConfiguredSecurityBuilder 它提供apply方法 再其内部调用add方法
public > C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
?add(configurer),主要就是将其传入的WebSecurityConfigurer存入到 LinkedHashMap configures中,
主要代码 this.configurers.put(clazz, configs);
private > void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class extends SecurityConfigurer> clazz = (Class extends SecurityConfigurer>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
?当所有的 WebSecurityConfigurer 类型的配置 全部应用到 WebSecurity中去后 setFilterChainProxySecurityConfigurer方法也就结束了
?回到创建过滤器链的方法 springSecurityFilterChain()
?它会判断我们刚刚的webSecurityConfigurers是否存在,不存在就新建一个,然后执行 webSecurity.build() 重要!
@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();
}
?最终内部会有下面这段代码, 主要关注 init() configure() 和 performBuild() 这三个方法
@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;
}
}
?init() 内部循环遍历 所有的 WebSecurityConfigurer ,它会执行到 WebSecurityConfigurerAdapter的
private void init() throws Exception {
Collection> configurers = getConfigurers();
for (SecurityConfigurer configurer : configurers) {
configurer.init((B) this);
}for (SecurityConfigurer configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
?configurer.init((B) this)
?它只要完成两件重要的事情:
?初始化HttpSecurity对象(注意它和WebSecurity不一样 );
?设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中;
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
初始化HttpSecurity对象在getHttp()方法中实现:
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, Object> sharedObjects = createSharedObjects();
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 defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
?从代码中可以了解,HttpSecurity是直接被new出来的,在创建HttpSecurity之前,首先初始化了AuthenticationManagerBuilder对象,这里有段代码很重要就是: AuthenticationManager authenticationManager = authenticationManager(); ,它创建AuthenticationManager实例,打开authenticationManager()方法:
?默认实现是在 WebSecurityConfigurerAdapter 中
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
?1、个性化配置入口之configure(AuthenticationManagerBuilder auth)
?我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。
文章图片
Xnip20200118_204438.png ?如下是自己继承WebSecurityConfigurerAdapter 重写 configure(AuthenticationManagerBuilder auth),实现个性化的第一个配置入口
/**
* @author johnny
* @create 2020-01-18 下午6:40
**/
@Configuration
@Slf4j
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
log.info("【测试 定制化入口configure(AuthenticationManagerBuilder auth)的执行 】");
}
}
?构建完HttpSecurity实例后,默认情况下会添加默认的拦截其配置:
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
?我挑一个默认的方法展开看一下比如 会话管理的sessionManagement(),内部就是去创建SessionManagementConfigurer并应用它
public SessionManagementConfigurer sessionManagement() throws Exception {
return getOrApply(new SessionManagementConfigurer<>());
}
?getOrApply 最有一句代码 return apply(configurer);
private > C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}
?apply(configurer) 注意这里的 configurer传入的是SessionManagementConfigurer
public > C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
?最终又调用了 add(configurer); 这不过这里是给 HttpSecurity的 configurers 配置初始的,上面是配置的WebSecurity的configurers, 不要混淆,最终这些configurers会被一个个创建成 对应的过滤器Filter的 详细在后面有说明
private > void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class extends SecurityConfigurer> clazz = (Class extends SecurityConfigurer>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
?如下图:为HttpSecurity添加了很多默认的配置
文章图片
Xnip20200118_205631.png ?回到 getHttp()方法
?最后调用configure(http); ,这又是一个可个性化的配置入口,它的默认实现是:WebSecurityConfigurerAdapter提供的
?默认的配置是拦截所有的请求需要认证之后才能访问,如果没有认证,会自动生成一个认证表单要求输入用户名和密码。
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();
}
?2、个性化配置入口之configure(HttpSecurity http)
?我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。
?OK,目前为止HttpSecurity已经被初始化,接下去需要设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中:
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
?当所有的WebSecurityConfigurer的init方法被调用之后,webSecurity.init()工作就结束了
?接下去调用了webSecurity.configure(),该方法同样是在AbstractConfiguredSecurityBuilder中实现的:
private void configure() throws Exception {
Collection> configurers = getConfigurers();
for (SecurityConfigurer configurer : configurers) {
configurer.configure((B) this);
}
}
?它的主要工作是迭代调用所有WebSecurityConfigurer的configurer方法,参数是WebSeucrity本身,这又是另外一个重要的个性化入口:
?3、个性化配置入口之configure(WebSecurity web)
?我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity。
?至此,三个重要的个性化入口都已经被调用,即在实现WebSecurityConfigurerAdapter经常需要重写的:
1、configure(AuthenticationManagerBuilder auth);
2、configure(WebSecurity web);
3、configure(HttpSecurity http);
?回到webSecurity构建过程,接下去重要的的调用:
O result = performBuild();
?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 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;
}
?首先计算出chainSize,也就是ignoredRequests.size() + securityFilterChainBuilders.size(); ,如果你不配置ignoredRequests,那就是securityFilterChainBuilders.size(),也就是HttpSecurity的个数,其本质上就是你一共配置几个WebSecurityConfigurerAdapter,因为每个WebSecurityConfigurerAdapter对应一个HttpSecurity,而所谓的ignoredRequests就是FilterChainProxy的请求,默认是没有的,如果你需要条跳过某些请求不需要认证或授权,可以如下配置:
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/statics/**");
}
?在上面配置中,所有以/statics开头请求都将被FilterChainProxy忽略。
?securityFilterChains.add(securityFilterChainBuilder.build()); 这一行就是初始化所有的过滤器,记得上面有段代码如下,将HttpSecurity设置到WebSecurity的 securityFilterChainBuilder中,上面就是调用HttpSecurity.build()方法,初始化所有的 HttpSecurity的过滤器链
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
?依然来到 doBuild()方法,只不过这次是执行的 HttpSecurity的
@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;
}
}
?重点查看 configure()该方法 会调用对应的 过滤器配置的configure()
如 再内部创建 SessionManagementFilter 最后添加到HttpSecurity中,也就是拿 HttpSecurity的configures 一个个创建出对应的过滤器
文章图片
Xnip20200118_205631.png
@Override
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);
}
}
?当doBuild()中的 configure(); 执行完毕后 的会得到如下HttpSecurity可以看到它内部的filters已经全部创建完毕
文章图片
Xnip20200118_211830.png ?回到doBuild()方法 该方中有 performBuild() 调用HttpSecurity的 performBuild(),默认实现如下,先对上面所有的过滤器进行排序,使用的是 FilterComparator() 进行排序的,这里不展开了,反正就是会排序成文章开始的那张图上面的顺序
@Override
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
?最后返回的是SecurityFilterChain的默认实现DefaultSecurityFilterChain。
?构建完所有SecurityFilterChain后,创建最为重要的FilterChainProxy实例,
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
文章图片
Xnip20200118_212246.png ?至此Spring Security 初始化完成,包括springSecurityFilterChain初始化,我们通过继承WebSecurityConfigurerAdapter来代达到个性化配置目的,文中提到了三个重要的个性化入口,并且WebSecurityConfigurerAdapter是可以配置多个的,其对应的接口就是会存在多个SecurityFilterChain实例,但是它们人仍然在同一个FilterChainProxy中,通过RequestMatcher来匹配并传入到对应的SecurityFilterChain中执行请求。
?5.个性化入口配置(扩展WebSecurityConfigurerAdapter)
?重要的个性化入口都是哪里调用的 已经在上面初始化 springSecurityFilterChain 源码中讲解了,这里知识总结一下
?1、个性化配置入口之configure(AuthenticationManagerBuilder auth)
?我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。
?2、个性化配置入口之configure(HttpSecurity http)
?我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。
?3、个性化配置入口之configure(WebSecurity web)
?我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity。
?实现WebSecurityConfigurerAdapter经常需要重写的:
1、configure(AuthenticationManagerBuilder auth);
2、configure(WebSecurity web);
3、configure(HttpSecurity http);
?6.总结
?本篇主要讲解了
?1.SpringBoot对于SpringSecurity的自动配置的支持类SecurityAutoConfiguration,
?2.核心注解@EnableWebSecurity
?3. SpringSecurity的核心过滤器链 springSecurityFilterChain 的初始化流程的源码
源码部分还是定下心来多看 加油!
个人博客网站 https://www.askajohnny.com 欢迎来访问!
本文由博客一文多发平台 OpenWrite 发布!
推荐阅读
- parallels|parallels desktop 解决网络初始化失败问题
- 迅捷流程图制作软件的使用方法!
- 代码不规范容易造成的bug
- Python专栏|数据分析的常规流程
- 2018-03-11|2018-03-11 存储过程
- 简述JavaEE学习流程
- 2.关于OpenGL|2.关于OpenGL 坐标系以及渲染流程
- Java并发编程|Java并发编程 - 深入剖析ReentrantLock之非公平锁加锁流程(第1篇)
- 开萌贝树婴幼儿用品店需要哪些流程
- 如何在facebook上打广告,流程是怎样的()