概述
spring security 源码分析系列文章。
源码分析
我们想一下,我们使用 ss 框架的步骤是怎么样的。
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailsService myUserDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { //spring security 放行注册中心健康检查 http.authorizeRequests() .antMatchers("/login/**", "/client/exit","/actuator/**","/assets/**").permitAll() // .anyRequest().authenticated() // 其他地址的访问均需验证权限 // .antMatchers("/hello**").hasAuthority("ROLE_ADMIN") .anyRequest().authenticated().and() .logout().deleteCookies("remove").invalidateHttpSession(false) .and() .formLogin() .loginPage("/login").and().csrf().disable().cors(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/assets/**"); } //认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean @Override public AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:8081","http://localhost:9090","http://localhost:8091","http://192.168.0.102:8082","http://localhost:8082")); configuration.setAllowedMethods(Arrays.asList("GET","POST")); configuration.setAllowedHeaders(Arrays.asList("x-requested-with")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
可以看到添加了 @EnableWebSecurity 的注解,同时创建了一个继承 WebSecurityConfigurerAdapter 。 @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; }
可以看到 import 了三个配置文件 。先来看看 WebSecurityConfiguration 类。 通过阅读类的注释我们知道以下消息
* - 使用 WebSecurity 去生成一个 FilterChainProxy
* - export 了几个必要的 beans
* - 自定义可以继承 WebSecurityConfigurerAdapter 来实现
下面我们来看一下 WebSecurityConfiguration 这个类内部执行了什么。
@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(); } /** * 创建一个 Spring Security Filter Chain * * Creates the Spring Security Filter Chain * @return * @throws Exception */ @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { /** * * 存在多个 webSecurityConfigurer 创建一个 WebSecurityConfigurerAdapter 之后走 webSecurity.apply * 最后执行 webSecurity.build()返回 Filter * */ 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(); } /** * 使用 SecurityConfigurer<FilterChainProxy, WebSecurityBuilder> 实例来创建一个 webSecurity * * 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 { //使用 @Value 获取到参数,见下面代码 //创建 webSecurity webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor)); if (debugEnabled != null) { webSecurity.debug(debugEnabled); } //依据@Order 排序 ,并检查排序 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; } //最后各个 SecurityConfigurer<Filter, WebSecurity> 都会放到一个调用同一个 webSecurity.apply 方法 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; } } final class AutowiredWebSecurityConfigurersIgnoreParents { private final ConfigurableListableBeanFactory beanFactory; public AutowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) { Assert.notNull(beanFactory, "beanFactory cannot be null"); this.beanFactory = beanFactory; } // 很简单,从一个 beanFactory 查找 webSecurityConfigurer @SuppressWarnings({ "rawtypes", "unchecked" }) 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; } }
可以看到内部维护这两个重要的字段 :
- List<SecurityConfigurer<Filter, WebSecurity>>
- WebSecurity
再利用他们来生成 Filter . SecurityConfigurer 看名字可以猜出是和配置相关的。 我们先看这个方法
@Autowired(required = false) public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { //创建 webSecurity webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor)); if (debugEnabled != null) { webSecurity.debug(debugEnabled); } //集合排序,以 order 大小 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; } //调用 webSecurity.apply 方法 for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { webSecurity.apply(webSecurityConfigurer); } //填充字段 this.webSecurityConfigurers = webSecurityConfigurers; }
而这个方法中的形参中的 securityConfigure 是从哪里来的呢? 即是 @value 里的值。
public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
同样在WebSecurityConfiguration 类内
@Bean public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) { return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory); }
进去 AutowiredWebSecurityConfigurersIgnoreParents 类看看。
final class AutowiredWebSecurityConfigurersIgnoreParents { private final ConfigurableListableBeanFactory beanFactory; public AutowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) { Assert.notNull(beanFactory, "beanFactory cannot be null"); this.beanFactory = beanFactory; } @SuppressWarnings({ "rawtypes", "unchecked" }) 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; } }
可以看到基本的逻辑就是从 beanFactory 中获取 SecurityConfigurer 的子类,这不是就是我们开始讲使用 Spring Security 时继承了一个 WebSecurityConfigurerAdapter 的类(该类可以对Spring Security 进行配置)。
通过上面的代码我们可以知道@EnableWebSecurity 会从环境中获取 SecurityConfigure 安全相关的配置文件,而最终生成一个Filter 类,内部调用的主要的是 WebSecurity 的 apply 和 build 方法。下面我们先深入 WebSecurity 这个类 。
WebSecurity
WebSecurity下文简称 ws, ws 的 build 非常重要而且也非常有意思。先来看看 ws 的类祖宗们。从名字可以大概猜出类的作用。
下面build 方法 ,下面把 AbstractConfigureSecurityBuilder 整个类贴出来,这个类比较重要,注意的她的 doBuild 方法还有apply 方法,顺便提一下 AbstractConfigureSecurityBuilder 同时还是 HttpSecurity 的父类,HttpSecurity 也是个狠角色,会在后面的文章介绍。
public interface SecurityBuilder<O> { /** * Builds the object and returns it or null. * * @return the Object to be built or null if the implementation allows it. * @throws Exception if an error occurred when building the Object */ O build() throws Exception; }
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> { private AtomicBoolean building = new AtomicBoolean(); private O object; /* * (non-Javadoc) * * @see org.springframework.security.config.annotation.SecurityBuilder#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"); } /** * Gets the object that was built. If it has not been built yet an Exception is * thrown. * * @return the Object that was built */ public final O getObject() { if (!this.building.get()) { throw new IllegalStateException("This object has not been built"); } return this.object; } /** * Subclasses should implement this to perform the build. * * @return the object that should be returned by {@link #build()}. * * @throws Exception if an error occurs */ protected abstract O doBuild() throws Exception; }
/* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.config.annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.util.Assert; import org.springframework.web.filter.DelegatingFilterProxy; /** * <p> * A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be applied to * it. This makes modifying the {@link SecurityBuilder} a strategy that can be customized * and broken up into a number of {@link SecurityConfigurer} objects that have more * specific goals than that of the {@link SecurityBuilder}. * </p> * * <p> * For example, a {@link SecurityBuilder} may build an {@link DelegatingFilterProxy}, but * a {@link SecurityConfigurer} might populate the {@link SecurityBuilder} with the * filters necessary for session management, form based login, authorization, etc. * </p> * * @see WebSecurity * * @author Rob Winch * * @param <O> The object that this builder returns * @param <B> The type of this builder (that is returned by the base class) */ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> { private final Log logger = LogFactory.getLog(getClass()); private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>(); private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<SecurityConfigurer<O, B>>(); private final Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>(); private final boolean allowConfigurersOfSameType; private BuildState buildState = BuildState.UNBUILT; private ObjectPostProcessor<Object> objectPostProcessor; /*** * Creates a new instance with the provided {@link ObjectPostProcessor}. This post * processor must support Object since there are many types of objects that may be * post processed. * * @param objectPostProcessor the {@link ObjectPostProcessor} to use */ protected AbstractConfiguredSecurityBuilder( ObjectPostProcessor<Object> objectPostProcessor) { this(objectPostProcessor, false); } /*** * Creates a new instance with the provided {@link ObjectPostProcessor}. This post * processor must support Object since there are many types of objects that may be * post processed. * * @param objectPostProcessor the {@link ObjectPostProcessor} to use * @param allowConfigurersOfSameType if true, will not override other * {@link SecurityConfigurer}'s when performing apply */ protected AbstractConfiguredSecurityBuilder( ObjectPostProcessor<Object> objectPostProcessor, boolean allowConfigurersOfSameType) { Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null"); this.objectPostProcessor = objectPostProcessor; this.allowConfigurersOfSameType = allowConfigurersOfSameType; } /** * Similar to {@link #build()} and {@link #getObject()} but checks the state to * determine if {@link #build()} needs to be called first. * * @return the result of {@link #build()} or {@link #getObject()}. If an error occurs * while building, returns null. */ public O getOrBuild() { if (isUnbuilt()) { try { return build(); } catch (Exception e) { logger.debug("Failed to perform build. Returning null", e); return null; } } else { return getObject(); } } /** * Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and * invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}. * * @param configurer * @return the {@link SecurityConfigurerAdapter} for further customizations * @throws Exception */ @SuppressWarnings("unchecked") public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this); add(configurer); return configurer; } /** * Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any * {@link SecurityConfigurer} of the exact same class. Note that object hierarchies * are not considered. * * @param configurer * @return the {@link SecurityConfigurerAdapter} for further customizations * @throws Exception */ public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; } /** * Sets an object that is shared by multiple {@link SecurityConfigurer}. * * @param sharedType the Class to key the shared object by. * @param object the Object to store */ @SuppressWarnings("unchecked") public <C> void setSharedObject(Class<C> sharedType, C object) { this.sharedObjects.put(sharedType, object); } /** * Gets a shared Object. Note that object heirarchies are not considered. * * @param sharedType the type of the shared Object * @return the shared Object or null if it is not found */ @SuppressWarnings("unchecked") public <C> C getSharedObject(Class<C> sharedType) { return (C) this.sharedObjects.get(sharedType); } /** * Gets the shared objects * @return the shared Objects */ public Map<Class<? extends Object>, Object> getSharedObjects() { return Collections.unmodifiableMap(this.sharedObjects); } /** * Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking * {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary. * * @param configurer the {@link SecurityConfigurer} to add * @throws Exception if an error occurs */ @SuppressWarnings("unchecked") private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception { 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<SecurityConfigurer<O, B>>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } } /** * Gets all the {@link SecurityConfigurer} instances by its class name or an empty * List if not found. Note that object hierarchies are not considered. * * @param clazz the {@link SecurityConfigurer} class to look for * @return a list of {@link SecurityConfigurer}s for further customization */ @SuppressWarnings("unchecked") public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) { List<C> configs = (List<C>) this.configurers.get(clazz); if (configs == null) { return new ArrayList<>(); } return new ArrayList<>(configs); } /** * Removes all the {@link SecurityConfigurer} instances by its class name or an empty * List if not found. Note that object hierarchies are not considered. * * @param clazz the {@link SecurityConfigurer} class to look for * @return a list of {@link SecurityConfigurer}s for further customization */ @SuppressWarnings("unchecked") public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) { List<C> configs = (List<C>) this.configurers.remove(clazz); if (configs == null) { return new ArrayList<>(); } return new ArrayList<>(configs); } /** * Gets the {@link SecurityConfigurer} by its class name or <code>null</code> if not * found. Note that object hierarchies are not considered. * * @param clazz * @return the {@link SecurityConfigurer} for further customizations */ @SuppressWarnings("unchecked") public <C extends SecurityConfigurer<O, B>> C getConfigurer(Class<C> clazz) { List<SecurityConfigurer<O, B>> configs = this.configurers.get(clazz); if (configs == null) { return null; } if (configs.size() != 1) { throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs); } return (C) configs.get(0); } /** * Removes and returns the {@link SecurityConfigurer} by its class name or * <code>null</code> if not found. Note that object hierarchies are not considered. * * @param clazz * @return */ @SuppressWarnings("unchecked") public <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) { List<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz); if (configs == null) { return null; } if (configs.size() != 1) { throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs); } return (C) configs.get(0); } /** * Specifies the {@link ObjectPostProcessor} to use. * @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null * @return the {@link SecurityBuilder} for further customizations */ @SuppressWarnings("unchecked") public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) { Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null"); this.objectPostProcessor = objectPostProcessor; return (O) this; } /** * Performs post processing of an object. The default is to delegate to the * {@link ObjectPostProcessor}. * * @param object the Object to post process * @return the possibly modified Object to use */ protected <P> P postProcess(P object) { return this.objectPostProcessor.postProcess(object); } /** * Executes the build using the {@link SecurityConfigurer}'s that have been applied * using the following steps: * * <ul> * <li>Invokes {@link #beforeInit()} for any subclass to hook into</li> * <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any * {@link SecurityConfigurer} that was applied to this builder.</li> * <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li> * <li>Invokes {@link #performBuild()} which actually builds the Object</li> * </ul> */ @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; } } /** * Invoked prior to invoking each {@link SecurityConfigurer#init(SecurityBuilder)} * method. Subclasses may override this method to hook into the lifecycle without * using a {@link SecurityConfigurer}. */ protected void beforeInit() throws Exception { } /** * Invoked prior to invoking each * {@link SecurityConfigurer#configure(SecurityBuilder)} method. Subclasses may * override this method to hook into the lifecycle without using a * {@link SecurityConfigurer}. */ protected void beforeConfigure() throws Exception { } /** * Subclasses must implement this method to build the object that is being returned. * * @return the Object to be buit or null if the implementation allows it */ protected abstract O performBuild() throws Exception; @SuppressWarnings("unchecked") 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); } } @SuppressWarnings("unchecked") private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.configure((B) this); } } private Collection<SecurityConfigurer<O, B>> getConfigurers() { List<SecurityConfigurer<O, B>> result = new ArrayList<SecurityConfigurer<O, B>>(); for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) { result.addAll(configs); } return result; } /** * Determines if the object is unbuilt. * @return true, if unbuilt else false */ private boolean isUnbuilt() { synchronized (configurers) { return buildState == BuildState.UNBUILT; } } /** * The build state for the application * * @author Rob Winch * @since 3.2 */ private static enum BuildState { /** * This is the state before the {@link Builder#build()} is invoked */ UNBUILT(0), /** * The state from when {@link Builder#build()} is first invoked until all the * {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked. */ INITIALIZING(1), /** * The state from after all {@link SecurityConfigurer#init(SecurityBuilder)} have * been invoked until after all the * {@link SecurityConfigurer#configure(SecurityBuilder)} methods have been * invoked. */ CONFIGURING(2), /** * From the point after all the * {@link SecurityConfigurer#configure(SecurityBuilder)} have completed to just * after {@link AbstractConfiguredSecurityBuilder#performBuild()}. */ BUILDING(3), /** * After the object has been completely built. */ BUILT(4); private final int order; BuildState(int order) { this.order = order; } public boolean isInitializing() { return INITIALIZING.order == order; } /** * Determines if the state is CONFIGURING or later * @return */ public boolean isConfigured() { return order >= CONFIGURING.order; } } }
通过上面分析,我们知道WebSecurity 执行 build 方法的时候时间会执行到父类的 doBuild 方法
@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 这两个方法都是子类继承的,ws 的这两个方法如下 :
@Override protected void beforeConfigure() throws Exception { setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build()); } protected void beforeInit() throws Exception { }
而父类的 init 方法和 configure 方法
private void init() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
//调用自身的init 方法 configurer.init((B) this); } for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) { configurer.init((B) this); } } private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
//调用自身的 configure 方法 configurer.configure((B) this); } } // this.configurers 是父类持有的一个字段 private Collection<SecurityConfigurer<O, B>> getConfigurers() { List<SecurityConfigurer<O, B>> result = new ArrayList<SecurityConfigurer<O, B>>(); for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) { result.addAll(configs); } return result; }
所以这里就要搞清楚,到底父类的 configurers 字段是什么时候赋值的呢?在apply 方法中,前面我们也见到了 ws 调用了apply方法,我们发现传进 apply 的是 WebSecurityConfigurerAdapter ,即是说将会调用 WebSecurityConfigurerAdapter 的 init 和 configure 方法。 WebSecurityConfigurerAdapter 这个类是不是好熟,这货就是我们在使用Spring Security 时候创建的类啊。
WebSecurityConfigurerAdapter 的 init 和 configure
public void init(final WebSecurity web) throws Exception { final HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() { public void run() { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); } }); } public void configure(WebSecurity web) throws Exception { } 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(); }
继续我们的调用,init 终于出现了我们 WebSecurity 的兄弟---HttpSecurity ,并调用了addSecurityFilterChainBuilder 方法。而 configure 的调用是第二个,即 WebSecurityConfigurerAdapter 没有实现。我们再看一下 ws 的 addSecurityFilterChainBuilder 是什么鬼。
public WebSecurity addSecurityFilterChainBuilder( SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) { this.securityFilterChainBuilders.add(securityFilterChainBuilder); return this; }
这里仅仅是为ws中赋值,那么这个值有什么作用呢?在 perferBuild 中会用到,下文会讲解,而HttpSecurity 会在后面的文章介绍,可以看到 init 方法还为 ws 设置了一个拦截器,此处本人还没研究但是我猜测这个拦截器是为了后面 Spring Security 做 mehtod 级别的权限拦截用的,因为文档中提到 method 级别的权限拦截正是用到拦截器来实现的。OK ,那么父类的 init 和 configure 方法就走完了哦,这时候到了 ws 的 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(" " + "******************************************************************** " + "********** Security debugging is enabled. ************* " + "********** This may include sensitive information. ************* " + "********** Do not use in a production system! ************* " + "******************************************************************** "); result = new DebugFilter(filterChainProxy); } postBuildAction.run(); return result; }
ignoredRequests 这个可以看到,有多少个ignoredRequests 就会生成多少个 securityFilterChain, 然后遍历 securityFilterChainBuilders 的每一项调用他们的build 的方法,build 方法会返回一个 securityFilterChain ,添加在 ws 的一个字段内,最后通过传入所有的 securityFilterChain 生成一个 FilterChainProxy ,可以知道 FilterChainProxy 必定是一个Filter ,通过名字可以它是一个代理类,这使我们想起了代理的设计模式。 好了,我们是不是漏了什么东西?是的,securityFilterChainBuilders 是什么东西?它是我们之前传入到 ws 的 HttpSecurity .而这里调用调用了securityFilterChainBuilders 的每一项调用他们的build 的方法,实际就是调用了 HttpSecurity 的 build 方法。
下一篇我们将要学习 HttpSecurity 这个类。
参考资料
- https://blog.csdn.net/qq_30905661/article/details/81082453
- https://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/#tech-intro-access-control (重要,要看)
- https://docs.spring.io/spring-security/site/docs/current/reference/html/authorization.html (认证)
- https://spring.io/guides/topicals/spring-security-architecture
- https://www.jianshu.com/u/fb66b7412d27 (spring security 原理分析过程,非常重要,要看)