赞
踩
从源码角度拆解SpringSecurity之核心流程
从源码角度拆解SpringSecurity之万能的SecurityBuilder
从源码角度拆解SpringSecurity之勤劳的FilterChainProxy
从源码角度拆解SpringSecurity之C位的AuthenticationManager
上一篇我们梳理了整个框架的核心流程,知道了SpringSecurity其实是基于Filter来实现的认证和权限管理,以及它是如何一步步被创建出来的。可是我们使用SpringSecurity框架时最特别的代码就是 “http.xxx().xxx().and().xxx()…” 了吧,这些语句其实就是我们定义的配置,那么这些配置又是如何被加载到框架中的呢,本章我们就一起来进行拆解吧。
文章所摘源码版本为spring-boot-starter-security:5.0.6.RELEASE
、spring-security-oauth2:2.3.5.RELEASE
上一章讲到构建Filter的配置类实际上是从IoC获取的,即通过 AutowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers方法来进行获取,而这个方法实质上又是从IoC中获取type为WebSecurityConfigurer.class的bean
//从IoC获取顶层配置类
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
接下来我们回忆下平时开发时的常用配置代码吧
//开启安全配置 @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { ... } //开启认证服务配置 @Configuration @EnableAuthorizationServer public class EndpointConfig extends AuthorizationServerConfigurerAdapter { ... } //开启资源配置 @Configuration @EnableResourceServer public class ResourceConfig extends ResourceServerConfigurerAdapter { ... }
第一个是开启SpringSecurity安全框架的必要配置,里面除了注入WebSecurityConfigurer之外还做了其他必要组件的初始化操作,第二个是我们需要使用SpringSecurity为我们封装Endpoint时要开启的配置,第三个是当我们需要对资源访问控制权限做控制时要开启的配置(似乎功能和第一个有重叠,甚至有些时候会造成理解歧义,故后续版本已将 @EnableResourceServer注解标记为过时
SecurityConfig实际上是WebSecurityConfigurer的派生类,而 @EnableWebSecurity 又包含了 @Configuration 注解
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
所以这实际上是往IoC注入了类型为WebSecurityConfigurer的bean。
再来看 @EnableAuthorizationServer,它import了 AuthorizationServerSecurityConfiguration配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//导入AuthorizationServerSecurityConfiguration配置类
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {
}
//又继承自WebSecurityConfigurerAdapter,如上文所说,该类是WebSecurityConfigurer的派生类
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
所以这也相当于是往IoC注入了类型为WebSecurityConfigurer的bean。
同理 @EnableResourceServer也是以同样的逻辑进行注入的,这里就不再赘述。
所以顶层配置有哪些,实际上是取决于我们需要配置哪些,具体配置的来龙去脉这下搞清楚了吧?
上一章我给大家留了一个小疑问,就是 AbstractConfiguredSecurityBuilder 中的 configurers 是怎么来的,接下来我们就揭晓答案。
//new出一个空Map等待子配置的加入 private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>(); //都是通过add方法往里put的,这是一个私有方法,其调用链均来自apply方法 private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception { ... synchronized (configurers) { ... this.configurers.put(clazz, configs); ... } } public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { ... add(configurer); return configurer; } public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; }
所以子配置其实均来自apply方法,还记得上一章讲的WebSecurityConfiguration.setFilterChainProxySecurityConfigurer方法吗,那里面其实就将顶层配置类通过apply方法put进去的,核心代码如下所示
package org.springframework.security.config.annotation.web.configuration; ... public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware { ... public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { ... for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { webSecurity.apply(webSecurityConfigurer); } ... } ... }
通过上文我们已经知道顶层配置是类型为WebSecurityConfigurer的bean,而无一例外他们都来自派生类WebSecurityConfigurerAdapter,接下来我们看看这个类到底有何乾坤吧
@Order(100) public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> { ... //如上文所说WebSecurity在执行doBuild时会依次调用顶层配置的init方法和configurer方法 public void init(final WebSecurity web) throws Exception { //先调用getHttp方法生成HttpSecurity final HttpSecurity http = getHttp(); ... } ... protected final HttpSecurity getHttp() throws Exception { if (http != null) { return http; } ... 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方法 configure(http); return http; } ... //提供configure的默认实现,该方法一般会被我们覆盖并加入自己的配置 protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); //spring的默认配置,如果不覆写(或覆写后调用super.configure)就会使用该配置 http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); } ... }
这个类比较长,摘取了关键部分(似乎还是比较长
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。