spring security 源码学习(三)WebSecurityConfiguration
本篇目标是解析WebSecurityConfiguration是如何初始化的
首先,看下他的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | /** * Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web * based security for Spring Security. It then exports the necessary beans. Customizations * can be made to {@link WebSecurity} by extending {@link WebSecurityConfigurerAdapter} * and exposing it as a {@link Configuration} or implementing * {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This * configuration is imported when using {@link EnableWebSecurity}. * * @see EnableWebSecurity * @see WebSecurity * * @author Rob Winch * @author Keesun Baik * @since 3.2 */ @Configuration public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware { private WebSecurity webSecurity; private Boolean debugEnabled; private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers; private ClassLoader beanClassLoader; @Autowired (required = false ) private ObjectPostProcessor<Object> objectObjectPostProcessor; @Bean public static DelegatingApplicationListener delegatingApplicationListener() { return new DelegatingApplicationListener(); } @Bean @DependsOn (AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() { return webSecurity.getExpressionHandler(); } /** * Creates the Spring Security Filter Chain * @return * @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(); } /** * Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP * tag support. * @return the {@link WebInvocationPrivilegeEvaluator} * @throws Exception */ @Bean @DependsOn (AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception { return webSecurity.getPrivilegeEvaluator(); } /** * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} * instances used to create the web configuration. * * @param objectPostProcessor the {@link ObjectPostProcessor} used to create a * {@link WebSecurity} instance * @param webSecurityConfigurers the * {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to * create the web configuration * @throws Exception */ @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); } Collections.sort(webSecurityConfigurers, 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; } @Bean public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) { return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory); } /** * A custom verision of the Spring provided AnnotationAwareOrderComparator that uses * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class * instances for the {@link Order} annotation. * * @author Rob Winch * @since 3.2 */ private static class AnnotationAwareOrderComparator extends OrderComparator { private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator(); @Override protected int getOrder(Object obj) { return lookupOrder(obj); } private static int lookupOrder(Object obj) { if (obj instanceof Ordered) { return ((Ordered) obj).getOrder(); } if (obj != null ) { Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass()); Order order = AnnotationUtils.findAnnotation(clazz, Order. class ); if (order != null ) { return order.value(); } } return Ordered.LOWEST_PRECEDENCE; } } /* * (non-Javadoc) * * @see org.springframework.context.annotation.ImportAware#setImportMetadata(org. * springframework.core.type.AnnotationMetadata) */ public void setImportMetadata(AnnotationMetadata importMetadata) { Map<String, Object> enableWebSecurityAttrMap = importMetadata .getAnnotationAttributes(EnableWebSecurity. class .getName()); AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes .fromMap(enableWebSecurityAttrMap); debugEnabled = enableWebSecurityAttrs.getBoolean( "debug" ); if (webSecurity != null ) { webSecurity.debug(debugEnabled); } } /* * (non-Javadoc) * * @see * org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java. * lang.ClassLoader) */ public void setBeanClassLoader(ClassLoader classLoader) { this .beanClassLoader = classLoader; } } |
如这个类开头的注释写的一样,这个类的最后会生成一个FilterChainProxy类(一个Fliter),作为过滤器(链)来处理一个请求进入spring后进行的认证操作。我们挑着这里面比较重要的步骤进行分析下。
(一)AutowiredWebSecurityConfigurersIgnoreParents初始化
1 2 3 4 5 | @Bean public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) { return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory); } |
这个类有一个获取所有WebSecurityConfigurer子类实例的方法
1 2 3 4 5 6 7 8 9 | public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() { List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>(); Map<String, WebSecurityConfigurer> beansOfType = beanFactory .getBeansOfType(WebSecurityConfigurer. class ); for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) { webSecurityConfigurers.add(entry.getValue()); } return webSecurityConfigurers; } |
这个类的用途在下面
(二)WebSecurity实例化以及基本配置的设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | @Autowired (required = false ) public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor,<br> //利用上面的初始化的AutowiredWebSecurityConfigurersIgnoreParents的getWebSecurityConfigurers方法获取我们的Spring security的配置 @Value ( "#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}" ) List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {<br> //创建WebSecurity实例 webSecurity = objectPostProcessor .postProcess( new WebSecurity(objectPostProcessor)); if (debugEnabled != null ) { webSecurity.debug(debugEnabled); } Collections.sort(webSecurityConfigurers, 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; } |
这个方法主要的操作是实例化WebSecurity,并将Spring security的配置设置为他的webSecurityConfigurers属性的值
这边为了方便查看之前的配置,我们把配置类的代码也插入到这边
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers( "/" , "/home" ).permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage( "/login" ) .permitAll() .and() .logout() .permitAll(); } @Bean @Override public UserDetailsService userDetailsService() { UserDetails user = User.withDefaultPasswordEncoder() .username( "user" ) .password( "password" ) .roles( "USER" ) .build(); return new InMemoryUserDetailsManager(user); } } |
(三)springSecurityFilterChain初始化
1 2 3 4 5 6 7 8 9 10 11 12 | @Bean (name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception {<br> //step 1 boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess( new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); }<br> //step 2 return webSecurity.build(); } |
就是这个方法,将最核心的Spring security的过滤器(链)初始化了。
这个方法的第一步就是判断当前是否配置了webSecurityConfigurers,如果没有,则会生成一个默认的:new WebSecurityConfigurerAdapter(),这个也就可以解释我刚接触Spring security时的困惑(为什么引入了Spring security的依赖后,我基本的配置类没写,我的接口就不能调用了?答:这边给了个默认的配置)
第二步就会对webSecurity进行构建:webSecurity.build();
首先调用的是AbstractSecurityBuilder的build()方法
1 2 3 4 5 6 7 | public final O build() throws Exception { if ( this .building.compareAndSet( false , true )) { this .object = doBuild(); return this .object; } throw new AlreadyBuiltException( "This object has already been built" ); } |
然后调用doBuild()方法,doBuild()是在AbstractSecurityBuilder的子类AbstractConfiguredSecurityBuilder中实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @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; } } |
这是一个很典型的模版方法模式,其中的beforeInit()和beforeConfigure()皆为钩子方法,这里默认也没有任何实现,我们暂时不用关注,主要需要注意的是init()、configure()和performBuild()方法。下面我们一个个分析这几个方法。
1、init方法
1 2 3 4 5 6 7 8 9 10 11 | 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 ); } } |
这边的configurer.init((B) this);调用的是WebSecurityConfigurerAdapter的init方法,源码如下
1 2 3 4 5 6 7 8 9 10 | public void init( final WebSecurity web) throws Exception { final HttpSecurity http = getHttp();( 1 ) web.addSecurityFilterChainBuilder(http).postBuildAction( new Runnable() { public void run() { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor. class ); web.securityInterceptor(securityInterceptor); } });( 2 ) } |
第(1)步是初始化了HttpSecurity,第(2)步的前半部分web.addSecurityFilterChainBuilder(http)则将这个HttpSecurity加入到了webSecurity的
1 | private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>(); |
这个属性中,后面performBuild的时候会用到;
这里的第(1)步其实很关键,一下子初始化了2个核心。这里再多说一句,针对不同的WebSecurityConfigurerAdapter的实现类,也就是配置,他们各自的HttpSecurity是各自的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | protected final HttpSecurity getHttp() throws Exception { if (http != null ) { return http; } DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor .postProcess( new DefaultAuthenticationEventPublisher()); localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = authenticationManager();( 1 ) authenticationBuilder.parentAuthenticationManager(authenticationManager); Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects(); http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects);( 2 ) if (!disableDefaults) { // @formatter:off http .csrf().and()( 3 ) .addFilter( new WebAsyncManagerIntegrationFilter())( 4 ) .exceptionHandling().and()( 5 ) .headers().and()( 6 ) .sessionManagement().and()( 7 ) .securityContext().and()( 8 ) .requestCache().and()( 9 ) .anonymous().and()( 10 ) .servletApi().and()( 11 ) .apply( new DefaultLoginPageConfigurer<HttpSecurity>()).and()( 12 ) .logout();( 13 ) // @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; } |
这个方法的第(1)步获取了AuthenticationManager,而如果AuthenticationManager没有被初始化,则这里则对AuthenticationManager进行了初始化,后面的第(2)步则实例化了HttpSecurity,第(3)-(13)步则给HttpSecurity加了些configure和给WebSecurity加了些Filter。下面我们着重看第(1)步,后面的几步会在后面的不同Filter的文章中分别交代。下面看下authenticationManager()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | protected AuthenticationManager authenticationManager() throws Exception { if (!authenticationManagerInitialized) {<br> //提供了自定义配置的地方,如果在我们的配置类中override了configure(AuthenticationManagerBuilder auth)方法,则就是在这里生效的 configure(localConfigureAuthenticationBldr); if (disableLocalConfigureAuthenticationBldr) {<br> //提供了默认的配置AuthenticationManager的方法,一般我们什么都不配置,则走的这里的方法 authenticationManager = authenticationConfiguration .getAuthenticationManager(); } else { authenticationManager = localConfigureAuthenticationBldr.build(); } authenticationManagerInitialized = true ; } return authenticationManager; } |
这个方法的作用写在了注释中,我们继续看authenticationConfiguration.getAuthenticationManager();方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public AuthenticationManager getAuthenticationManager() throws Exception { if ( this .authenticationManagerInitialized) { return this .authenticationManager; }<br> //new了一个AuthenticationManagerBuilder实例,执行了new AuthenticationManagerBuilder(objectPostProcessor) AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder( this .objectPostProcessor); if ( this .buildingAuthenticationManager.getAndSet( true )) { return new AuthenticationManagerDelegator(authBuilder); } for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {( 1 ) authBuilder.apply(config); } authenticationManager = authBuilder.build();( 2 ) if (authenticationManager == null ) { authenticationManager = getAuthenticationManagerBean(); } this .authenticationManagerInitialized = true ; return authenticationManager; } |
这边核心的有2步,第(1)步是获取配置,第(2)步是我们熟悉的初始化节奏。而第一步的配置,默认有如下的配置,他们具体从哪来的,我们留到后面再讲解。
那么第(2)步就来到了AuthenticationManagerBuilder的build(),继而就是doBuild(),也就是我们上面的模版方法,这边我们再贴一次代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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; } } |
这边最终返回的是ProviderManager,上面的init和configur我们先跳过分析,直接看这边的performBuild()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | protected ProviderManager performBuild() throws Exception { if (!isConfigured()) { logger.debug( "No authenticationProviders and no parentAuthenticationManager defined. Returning null." ); return null ; } ProviderManager providerManager = new ProviderManager(authenticationProviders, parentAuthenticationManager); if (eraseCredentials != null ) { providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials); } if (eventPublisher != null ) { providerManager.setAuthenticationEventPublisher(eventPublisher); } providerManager = postProcess(providerManager); return providerManager; } |
这边可以看的出来ProviderManager是new出来的,而他最重要的则是后面的参数authenticationProviders,也就是List<AuthenticationProvider> authenticationProviders,他就是进行鉴权的核心实现。
AuthenticationManager的初始化,我们就先到这里,后面会有一篇文章再对这个过程进行详细梳理。
2、configure(WebSecurity web)方法
这边是对WebSecurity进行定制化的地方,这个方法相对init()来说就简单多了
默认来讲,如果我们在我们的配置类中未override这个方法,那么这里将什么都不发生,默认实现如下。
1 2 | public void configure(WebSecurity web) throws Exception { } |
这里我们可以配置一些需要忽略的,也就是不需要认证的请求,如:
1 2 3 4 | @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers( "/test/**" ); } |
这边的配置和下面的ignoredRequests.size()相对应。
3、performBuild()
这个方法将会真正的实例化Filter并返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | @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<SecurityFilterChain>( chainSize); for (RequestMatcher ignoredRequest : ignoredRequests) { securityFilterChains.add( new DefaultSecurityFilterChain(ignoredRequest)); }<br> // for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build());( 1 ) } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);( 2 ) 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; } |
这里的第(1)步则是实例化SecurityFilterChain,加入到List<SecurityFilterChain> filterChains中,这里的securityFilterChainBuilder.build()实质上就是HttpSecurity进行build,这里的list中有多少值就看系统配置了多少个WebSecurityConfigurerAdapter,按照我们之前的配置,这里就一个。根据下面的类图,也就是调用的AbstractSecurityBuilder的build()方法
HttpSecurity的build我们发到下一篇文章。
第(2)步则将List<SecurityFilterChain> filterChains作为FilterChainProxy初始化的属性,初始化了FilterChainProxy,并返回了这个Filter。
至此,FilterChainProxy的初始化结束了,也就是Spring security的初始化结束了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构