spring security 源码学习(三)WebSecurityConfiguration
本篇目标是解析WebSecurityConfiguration是如何初始化的
首先,看下他的源码。
/** * 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初始化
@Bean public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) { return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory); }
这个类有一个获取所有WebSecurityConfigurer子类实例的方法
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实例化以及基本配置的设置
@Autowired(required = false) public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor,
//利用上面的初始化的AutowiredWebSecurityConfigurersIgnoreParents的getWebSecurityConfigurers方法获取我们的Spring security的配置 @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
//创建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属性的值
这边为了方便查看之前的配置,我们把配置类的代码也插入到这边
@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初始化
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception {
//step 1 boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); }
//step 2 return webSecurity.build(); }
就是这个方法,将最核心的Spring security的过滤器(链)初始化了。
这个方法的第一步就是判断当前是否配置了webSecurityConfigurers,如果没有,则会生成一个默认的:new WebSecurityConfigurerAdapter(),这个也就可以解释我刚接触Spring security时的困惑(为什么引入了Spring security的依赖后,我基本的配置类没写,我的接口就不能调用了?答:这边给了个默认的配置)
第二步就会对webSecurity进行构建:webSecurity.build();
首先调用的是AbstractSecurityBuilder的build()方法
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中实现的
@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方法
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方法,源码如下
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的
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
这个属性中,后面performBuild的时候会用到;
这里的第(1)步其实很关键,一下子初始化了2个核心。这里再多说一句,针对不同的WebSecurityConfigurerAdapter的实现类,也就是配置,他们各自的HttpSecurity是各自的。
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()方法
protected AuthenticationManager authenticationManager() throws Exception { if (!authenticationManagerInitialized) {
//提供了自定义配置的地方,如果在我们的配置类中override了configure(AuthenticationManagerBuilder auth)方法,则就是在这里生效的 configure(localConfigureAuthenticationBldr); if (disableLocalConfigureAuthenticationBldr) {
//提供了默认的配置AuthenticationManager的方法,一般我们什么都不配置,则走的这里的方法 authenticationManager = authenticationConfiguration .getAuthenticationManager(); } else { authenticationManager = localConfigureAuthenticationBldr.build(); } authenticationManagerInitialized = true; } return authenticationManager; }
这个方法的作用写在了注释中,我们继续看authenticationConfiguration.getAuthenticationManager();方法
public AuthenticationManager getAuthenticationManager() throws Exception { if (this.authenticationManagerInitialized) { return this.authenticationManager; }
//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(),也就是我们上面的模版方法,这边我们再贴一次代码
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()
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这个方法,那么这里将什么都不发生,默认实现如下。
public void configure(WebSecurity web) throws Exception { }
这里我们可以配置一些需要忽略的,也就是不需要认证的请求,如:
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/test/**"); }
这边的配置和下面的ignoredRequests.size()相对应。
3、performBuild()
这个方法将会真正的实例化Filter并返回
@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)); }
// 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的初始化结束了。