当前位置: 首页 > 后端技术 > Java

SpringSecurity的初始化流程(一)

时间:2023-04-01 15:51:18 Java

SpringSecurity可以通过xml配置或者注解来初始化。其实配置方法不同,初始化过程大致相同。所以我们只通过注解来分析初始化。注解方法的初始化也会根据你项目使用的框架不同而不同。我们只分析SpringBoot框架下SpringSecurity的初始化过程。下面简单列举一下Spring.faciries文件中配置的SpringSecurity相关的自动组装类,只是一个直观的印象,就不一一分析了:org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\org.springframework.boot.autoconfigure.security.servlet.UserDetailsS??erviceAutoConfiguration,\org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfigurationf,\org.raboot.autoconfigure.security.reactive.ReactiveUserDetailsS??erviceAutoConfiguration,\org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\org.springbootframeauto.config.SendGridAutoConfiguration,\org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfigurati上,\org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\org.springframework.boot.autoconfigure.sec.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\SpringSecurity的初始化过程确实复杂,配置类也很多。一开始,有点难下手。因此,我决定从官网的文档入手。参考:https://docs.spring.io/spring…。通过阅读文档,我们知道SpringSecurity是通过过滤器实现的。filter的生效流程如下图所示,其中SpringSecurity会配置n个filter来达到目的:SpringSecurity的多个filter并不是并行插入到Servlet的filter链中,而是插入以过滤器的形式。filter是DelegatingFilterProxy:DelegatingFilterProxy包含了一个由SecurityFilterChain:组成的FilterChainProxy,而正是这个SecurityFilterChain包含了一系列用于实现SpringSecurity的filter过滤器:通过上面的分析,我们知道SpringSecurity是由FilterChainProxy中组装的SecurityFilterChain实现的,SecurityFilterChain包含一系列的过滤器,而正是这些过滤器实现了SpringSecurity的所有功能。因此,第一步从源码的角度分析一下SpringSecurity的SecurityFilterChain的初始化过程。SpringSecurity的配置类不需要像SpringBoot框架下的@EnableWebSecurity那样开启。SpringBoot的自动组装机制会自动启用SpringSecurity的配置。HttpSecutiry类结构SpringSecurity的Filter最终是通过SecurityBuilder创建的,SecurityBuilder持有各个过滤器的configurator,最后通过configurator生成过滤器并组装成SecurityFilterChain。HttpSecurityConfigurationHttpSecurityConfiguration是SpringSecurity的一个重要配置类。它的重要功能之一是初始化HttpSecurity对象。通过httpSecurity()方法创建:@Bean(HTTPSECURITY_BEAN_NAME)@Scope("prototype")HttpSecurityhttpSecurity()throwsException{WebSecurityConfigurerAdapter.LazyPasswordEncoderpasswordEncoder=newWebSecurityConfigurerAdapter.LazyPasswordEncoder(this.context);AuthenticationManagerBuilderauthenticationBuilder=newWebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor,passwordEncoder);authenticationBuilder.parentAuthenticationManager(authenticationManager());HttpSecurityhttp=newHttpSecurity(this.objectPostProcessor,authenticationBuilder,createSharedObjects());//@formatter:offhttp.csrf(withDefaults()).addFilter(newWebAsyncManagerIntegrationFilter()).exceptionHandling(withDefaults()).headers(withDefaults()).sessionManagement(withDefaults()).securityContext(withDefaults()).requestCache(withDefaults()).anonymous(withDefaults()).servletApi(withDefaults()).apply(newDefaultLoginPageConfigurer<>());http.logout(withDefaults());//@formatter:返回http;}创建一个HttpSecurity对象并调用一系列方法来组装configurers属性。配置器是安全过滤器配置器的集合。SpringSecurity过滤器是通过相应的配置器生成的,所以我们的目光首先关注上面生成配置器的代码。比如http.csrf(withDefaults())调用会生成SpringSecurity的防跨域请求过滤器的配置器,最后通过配置器生成过滤器(后面会分析源码)。SpringSecurity的其他过滤器也是采用上述方法,先生成过滤器配置器,然后配置器生成过滤器,再将过滤器组装到SecurityFilterChain中生效。具体生成configurator的代码就不一一贴出来了。HttpSecurityConfiguration的其他初始化过程暂时忽略。SpringBootWebSecurityConfigurationSpringBootWebSecurityConfiguration只有一个方法defaultSecurityFilterChain,用来创建SecurityFilterChain:@Configuration(proxyBeanMethods=false)@ConditionalOnDefaultWebSecurity@ConditionalOnWebApplication(type=Type.SERVLET)classSpringBootWebSecurityConfiguration{@Bean@Order(SecurityProperties.BASIC_AUTH_ORDER)SecurityFilterChaindefaultSecurityFilterChain(HttpSecurityhttp)throwsException{http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();返回http.build();}}defaultSecurityFilterChain方法有一个参数:HttpSecurity,对象的初始化过程前面我们已经分析过了,我们直接看http.build()。AbstractSecurityBuilder#build()构建方法在父类AbstractSecurityBuilde中实现:@OverridepublicfinalObuild()throwsException{if(this.building.compareAndSet(false,true)){this.object=doBuild();返回这个。目的;}thrownewAlreadyBuiltException("Thisobjecthasalreadybeenbuilt");}AbstractConfiguredSecurityBuilder#doBuilddoBuild方法的一系列调用方式:@OverrideprotectedfinalOdoBuild()throwsException{synchronized(this.configurers){this.buildState=BuildState.INITIALIZING;初始化前();在里面();this.buildState=BuildState.CONFIGURING;配置前();配置();this.buildState=BuildState.BUILDING;Oresult=performBuild();;返回结果;我们关注两个,一个是configure(),另一个是performBuild()。AbstractConfiguredSecurityBuilder#configure()configure方法获取configures,逐一调用其configure方法。configures我们之前分析过HttpSecurityConfiguration的源码,是在HttpSecurity创建之后组装的。privatevoidconfigure()throwsException{Collection>configurers=getConfigurers();for(SecurityConfigurerconfigurer:configurers){configurer.configure((B)this);}}组装进来的众多配置器我们暂时不一一分析。下面以跨域过滤器配置器为例,了解过滤器的创建和配置过程。其他配置器的配置过程类似:@Overridepublicvoidconfigure(Hhttp){CsrfFilterfilter=newCsrfFilter(this.csrfTokenRepository);...省略n行代码filter=postProcess(filter);http.addFilter(过滤器);可以看到configure方法创建了一个filter,并将filter添加到HttpSecurity的filters容器中。快速浏览一下addFilter方法:@OverridepublicHttpSecurityaddFilter(Filterfilter){Integerorder=this.filterOrders.getOrder(filter.getClass());if(order==null){thrownewIllegalArgumentException("过滤器类"+filter.getClass().getName()+"没有注册的顺序,没有指定的顺序不能添加。考虑使用addFilterBefore或addFilterAfter代替”);}this.filters.add(newOrderedFilter(filter,order));归还这个;我们只需要知道所有的过滤器在加入过滤器的时候是有顺序的,这个顺序是在HttpSecurity初始化的时候指定的。好了,至此,我们知道过滤器已经添加到构建器的容器过滤器中了。接下来,我们可以猜测,这一定是创建者想要将他的过滤器中的过滤器组装到SecurityFilterChans中的方式。继续分析……HttpSecurity#performBuilder()performBuilder()方法回到HttpSecurity实现,将创建的filter过滤器作为参数,创建一个DefaultSecurityFilterChain:@OverrideprotectedDefaultSecurityFilterChainperformBuild(){this.filters.排序(OrderComparator.INSTANCE);ListsortedFilters=newArrayList<>(this.filters.size());for(Filterfilter:this.filters){sortedFilters.add(((OrderedFilter)filter).filter);}返回新的DefaultSecurityFilterChain(this.requestMatcher,sortedFilters);}DefaultSecurityFilterChain是我们的主角!将创建的DefaultSecurityFilterChain注入到Spring的Ioc容器中,再组装到FilterChainProxy中生效。下次再分解!!!上一篇Spring事务控制AOP包围切入底层原理下一篇SpringSecurity的初始化过程(二)