• Spring Security 源码分析 --- WebSecurity


    概述

           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 的类祖宗们。从名字可以大概猜出类的作用。

          Image

          下面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 这个类。

         

    参考资料

  • 相关阅读:
    C#解析Json数据(利用Newtonsoft.Json库)
    巧妙解决百度地图加偏纠偏问题
    sql 日期时间函数+格式转换
    遇到sql server 遇到以零作除数错误
    js实现鼠标移到链接文字弹出一个提示层的方法
    sql语句 如何判断A表中的a列数据是否在B表中的b列中存在
    MVC使用Gantt Chart实现甘特图,管理事情进度
    jQueryGantt—集变态与惊艳于一身
    自动化测试
    小程序测试及一些限制
  • 原文地址:https://www.cnblogs.com/Benjious/p/11182490.html
Copyright © 2020-2023  润新知