本篇主要讲解 SpringSecurity初始化流程的源码部分,包括核心的 springSecurityFilterChain 是如何创建的,以及在介绍哪里可以扩展个性化的配置,SpringSecurity源码其实是蛮难得 各种Builder Configure 看得真的头疼!
SpringSecurity 的核心功能主要包括:
认证 (你是谁)
授权 (你能干什么)
攻击防护 (防止伪造身份)
其核心就是一组过滤器链,项目启动后将会自动配置,本篇也会涉及过滤器链是如何自动初始化的。
SecurityContextPersistenceFilter是最前面的一个filter
请求到它时候会去检查 根据sessionId找到session 判断session 中是否存在 SecurityContext 在 则将 SecurityContext 存入当前的线程中去
响应的时候,看当前线程是否有SecurityContext ,如果有 放入到session中去 这样不同的请求都能拿到相同的 用户认证信息。
UsernamePasswordAuthenticationFilter该过滤器是处理表单登录的,通过表单登录提交的认证都会经过它处理
SocialAuthenticationFilter比如这个就是社交登录使用的Filter
详细可以看我另外一篇 SpringSocial 实现第三方QQ登录 SpringSocial 实现第三方QQ登录
如果是SpringBoot项目只要你依赖了SpringSecurity相关依赖依然会有自动配置类
SecurityAutoConfiguration生效 它会导入 WebSecurityEnableConfiguration
@EnableWebSecurity将会是我们本篇的主要切入点
该注解 它是初始化Spring Security的入口 .
打开@EnableWebSecurity注解
@Retention(value = 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的核心
打开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 && SecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); } return webSecurity.build(); }
从源码中知道过滤器通过最后的 webSecurity.build()创建,webSecurity的类型为:WebSecurity,它在 setFilterChainProxySecurityConfigurer方法中优先被创建了:
@Autowired(required = false) public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor)); if (debugEnabled != null) { webSecurity.debug(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) { webSecurity.apply(webSecurityConfigurer); } this.webSecurityConfigurers = webSecurityConfigurers; }
从代码中可以看到,它是直接被new出来的:
webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor));
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
**AutowiredWebSecurityConfigurersIgnoreParents的 getWebSecurityConfigurers()
如下,就是获取所有的 WebSecurityConfigurer的类型的配置类
而通常 我们通过继承 WebSecurityConfigurerAdapter 来自定义WebSecurityConfigurer**
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() { List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>(); Map<String, WebSecurityConfigurer> beansOfType = beanFactory .getBeansOfType(WebSecurityConfigurer.class); for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) { webSecurityConfigurers.add(entry.getValue()); } return webSecurityConfigurers; }
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { webSecurity.apply(webSecurityConfigurer); }
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; }
**add(configurer),主要就是将其传入的WebSecurityConfigurer存入到 LinkedHashMap configures中,
主要代码 this.configurers.put(clazz, configs);** private <C extends SecurityConfigurer<O, B>> void add(C configurer) { 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<>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } }
它会判断我们刚刚的webSecurityConfigurers是否存在,不存在就新建一个,然后执行 webSecurity.build() 重要!
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = webSecurityConfigurers != null && SecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); } return webSecurity.build(); }
@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; } }
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); } }
初始化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<Class<?>, 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<AbstractHttpConfigurer> 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; }
我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。
/** * @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<HttpSecurity> sessionManagement() throws Exception { return getOrApply(new SessionManagementConfigurer<>()); }
getOrApply 最有一句代码 return apply(configurer);
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> 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 extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this); add(configurer); return configurer; }
private <C extends SecurityConfigurer<O, B>> void add(C configurer) { 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<>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } }
如下图:为HttpSecurity添加了很多默认的配置
回到 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<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> 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();
@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; }
首先计算出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 一个个创建出对应的过滤器
@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已经全部创建完毕
回到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);
至此Spring Security 初始化完成,包括springSecurityFilterChain初始化,我们通过继承WebSecurityConfigurerAdapter来代达到个性化配置目的,文中提到了三个重要的个性化入口,并且WebSecurityConfigurerAdapter是可以配置多个的,其对应的接口就是会存在多个SecurityFilterChain实例,但是它们人仍然在同一个FilterChainProxy中,通过RequestMatcher来匹配并传入到对应的SecurityFilterChain中执行请求。
重要的个性化入口都是哪里调用的 已经在上面初始化 springSecurityFilterChain 源码中讲解了,这里知识总结一下
我们可以通过继承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);
**本篇主要讲解了
1.SpringBoot对于SpringSecurity的自动配置的支持类SecurityAutoConfiguration,
2.核心注解@EnableWebSecurity
3. SpringSecurity的核心过滤器链 springSecurityFilterChain 的初始化流程的源码
源码部分还是定下心来多看 加油!**
个人博客网站 https://www.askajohnny.com 欢迎来访问!
本文由博客一文多发平台 OpenWrite 发布!