美文网首页开源框架-SpringSecurity系列
Spring Security解析一:安全配置过程概览

Spring Security解析一:安全配置过程概览

作者: 一根线条 | 来源:发表于2020-03-05 11:08 被阅读0次

    在spring-security-config包中有个用于使用JavaConfig的方式启动框架的注解
    org.springframework.security.config.annotation.web.configuration.EnableWebSecurity

    /**
     * Add this annotation to an {@code @Configuration} class to have the Spring Security
     * configuration defined in any {@link WebSecurityConfigurer} or more likely by extending
     * the {@link WebSecurityConfigurerAdapter} base class and overriding individual methods:
     *
     * @see WebSecurityConfigurer
     * @see WebSecurityConfigurerAdapter
     *
     * @author Rob Winch
     * @since 3.2
     */
    @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
    @Target(value = { java.lang.annotation.ElementType.TYPE })
    @Documented
    @Import({ WebSecurityConfiguration.class,
            SpringWebMvcImportSelector.class,
            OAuth2ImportSelector.class,
            HttpSecurityConfiguration.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;
    }
    

    添加该注解到@Configuration的类上,应用程序便可以使用自定义的WebSecurityConfigurer或拓展自WebSecurityConfigurerAdapter的配置类来装配Spring Security框架。

    1,WebSecurityConfiguration配置类

    该配置类的目的是在启动时收集BeanFactory中所有的SecurityConfigurer和SecurityFilterChain,然后将收集的SecurityConfigurer和SecurityFilterChain交给WebSecurity来构造出名为springSecurityFilterChain的Filter(FilterChainProxy)并加入IOC容器,从而将Spring Security安全配置融入到应用中。

    /**
     * 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(proxyBeanMethods = false)
    public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
        private WebSecurity webSecurity;
    
        private Boolean debugEnabled;
    
        //收集保存所有的SecurityConfigurer配置
        //SecurityConfigurer:WebSecurityConfigurer、WebSecurityConfigurerAdapter
        private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
    
        private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
    
        private ClassLoader beanClassLoader;
    
        @Autowired(required = false)
        private ObjectPostProcessor<Object> objectObjectPostProcessor;
    
        //在后续的代码中会使用到,例如会话管理部分
        //其本身是个ApplicationListener,监听ApplicationEvent事件
        //然后将事件转发给内部注册的合适的SmartApplicationListener
        //注意:ApplicationEvent是一个比较顶级的事件类型,其下有各种具体类型的事件对象
        @Bean
        public static DelegatingApplicationListener delegatingApplicationListener() {
            return new DelegatingApplicationListener();
        }
    
        //当在jsp中使用security相关的标签时有用(需要引入spring-security-taglibs包)
        @Bean
        @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
        public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
            return webSecurity.getExpressionHandler();
        }
        /**
         * 注意:这是一个Setter方法并且添加了@Autowired注释,所以会最先执行;
         * SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>
         */
        @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);
            }
            //进行排序【可以有多个SecurityConfigurer,但是order不能有相同的】
            webSecurityConfigurers.sort(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途径之一(可致使不会使用默认的WebSecurityConfigurerAdapter进行装配)
            //注意:我们时常继承WebSecurityConfigurerAdapter来进行配置,而WebSecurityConfigurerAdapter
            //也是实现了SecurityConfigurerFilter, WebSecurity>接口。
            // 所以我们是可以有多个继承了WebSecurityConfigurerAdapter的配置类的哦~~
            for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
                //保存到webSecurity的configurers变量中
                webSecurity.apply(webSecurityConfigurer);
            }
            this.webSecurityConfigurers = webSecurityConfigurers;
        }
        
        @Autowired(required = false)
        void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
            securityFilterChains.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.securityFilterChains = securityFilterChains;
        }
    
        @Autowired(required = false)
        void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
            webSecurityCustomizers.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.webSecurityCustomizers = webSecurityCustomizers;
        }
    
        /**
         * Creates the Spring Security Filter Chain 
         *【默认的Filter的名称为springSecurityFilterChain】
         * 注意:在SpringBoot中通过自动化配置会通过该名称来创建DelegatingFilterProxyRegistrationBean,
         * 从而在初始化阶段被注册到Servlet容器中。
         */
        @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
        public Filter springSecurityFilterChain() throws Exception {
            //判断当前IOC中是否有SecurityConfigurer,如果没有则使用WebSecurityConfigurerAdapter作为默认配置
            //webSecurityConfigurers由上面的setFilterChainProxySecurityConfigurer方法执行后得出
            boolean hasConfigurers = webSecurityConfigurers != null
                    && !webSecurityConfigurers.isEmpty();
            boolean hasFilterChain = !securityFilterChains.isEmpty();
            if (!hasConfigurers && !hasFilterChain) {  //如果没有找到SecurityConfigurer类型的Bean则执行默认配置
                //使用WebSecurityConfigurerAdapter作为配置
                WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                        .postProcess(new WebSecurityConfigurerAdapter() {
                        });
                //添加到WebSecurity中
                webSecurity.apply(adapter);
            }
    
            //--------------------------------------------------------------------------------//
            // 构建前调整WebSecurity途径之一:将IOC容器中得到的所有SecurityFilterChain类型的Bean进行逐个处理
            for (SecurityFilterChain securityFilterChain : securityFilterChains) {
                //添加返回SecurityFilterChain的SecurityBuilder
                webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
                for (Filter filter : securityFilterChain.getFilters()) {
                    if (filter instanceof FilterSecurityInterceptor) {
                        webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
                        break;
                    }
                }
            }
            
            //构建前调整WebSecurity途径之一:对WebSecurity进行自定义
            for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
                customizer.customize(this.webSecurity);
            }
            //--------------------------------------------------------------------------------//
    
            //构建过滤器链
            return webSecurity.build();
        }
    
        @Bean
        public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
                ConfigurableListableBeanFactory beanFactory) {
            return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
        }
    }
    
    AutowiredWebSecurityConfigurersIgnoreParents
    /**
     * A class used to get all the {@link WebSecurityConfigurer} instances from the current
     * {@link ApplicationContext} but ignoring the parent.
     *
     * @author Rob Winch
     *
     */
    final class AutowiredWebSecurityConfigurersIgnoreParents {
        //从BeanFactory中得到所有WebSecurityConfigurer类型的Bean
        public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
            List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
            //返回Bean的名称和Bean实例
            Map<String, WebSecurityConfigurer> beansOfType = beanFactory
                    .getBeansOfType(WebSecurityConfigurer.class);
            for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {                
                //保存所有WebSecurityConfigurer类型的Bean
                webSecurityConfigurers.add(entry.getValue());
            }
            return webSecurityConfigurers;
        }
    }
    

    2,WebSecurity

    由于继承关系,当调用WebSecurity的build()方法时其实是在执行AbstractConfiguredSecurityBuilder中的doBuild()方法, doBuild()方法是个模板方法,里面依次调用了beforeInit()、init()、beforeConfigure()、configure()和performBuild()方法并返回结果。

    1. beforeInit()与beforeConfigure()两个方法默认不做任何事情;
    2. init()与configure()两个方法默认是以WebSecurity实例为参数逐个执行上面过程中收集的SecurityConfigurer类型实例的init(builder)与configure(builder)方法;
    3. performBuild()方法实际是执行WebSecurity中的performBuild()方法;

    其继承关系图如下所示:

    WebSecurity继承关系
    public final class WebSecurity extends
            AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
            SecurityBuilder<Filter>, ApplicationContextAware {
        private final Log logger = LogFactory.getLog(getClass());
    
        private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
    
        //SecurityConfigurer类型实例执行完configure(builder)方法完成装配后将结果添加到该集合中
        private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<>();
    
        private IgnoredRequestConfigurer ignoredRequestRegistry;
    
        private FilterSecurityInterceptor filterSecurityInterceptor;
    
        private HttpFirewall httpFirewall;
    
        private boolean debugEnabled;
    
        //当在jsp中使用security相关的标签时有用(需要引入spring-security-taglibs包)
        private WebInvocationPrivilegeEvaluator privilegeEvaluator;
    
        private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
    
        private SecurityExpressionHandler<FilterInvocation> expressionHandler = defaultWebSecurityExpressionHandler;
    
        private Runnable postBuildAction = () -> {
        };
    
        //SecurityConfigurerAdapter中会设置该Runnable,
        //并在实现中调用下面的securityInterceptor设置拦截器
        public WebSecurity postBuildAction(Runnable postBuildAction) {
            this.postBuildAction = postBuildAction;
            return this;
        }
    
        /**
         * Sets the {@link FilterSecurityInterceptor}. This is typically invoked by
         * {@link WebSecurityConfigurerAdapter}.
         * @param securityInterceptor the {@link FilterSecurityInterceptor} to use
         * @return the {@link WebSecurity} for further customizations
         */
        public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
            this.filterSecurityInterceptor = securityInterceptor;
            return this;
        }
    
        /**
         * 该方法被 WebSecurityConfiguration 调用创建bean
         */
        public WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {
            if (privilegeEvaluator != null) {
                return privilegeEvaluator;
            }
            return filterSecurityInterceptor == null ? null
                    : new DefaultWebInvocationPrivilegeEvaluator(filterSecurityInterceptor);
        }
    
        /**
         * Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not specified,
         * then a {@link DefaultWebInvocationPrivilegeEvaluator} will be created when
         * {@link #securityInterceptor(FilterSecurityInterceptor)} is non null.
         *
         * @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use
         * @return the {@link WebSecurity} for further customizations
         */
        public WebSecurity privilegeEvaluator(
                WebInvocationPrivilegeEvaluator privilegeEvaluator) {
            this.privilegeEvaluator = privilegeEvaluator;
            return this;
        }
    
    
        @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("\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;
        }
    }
    

    在这里完成了名为springSecurityFilterChain的FilterChainProxy的创建(本身是个Filter),它是SecurityFilterChain的代理对象,里面包含了多个SecurityFilterChain的集合。

    后面通过DelegatingFilterProxyRegistrationBean【ServletContextInitializer类型】实例对象来将这里的FilterChainProxy添加到Servlet容器中,从而实现请求的过滤操作

    附:SecurityBuilder与WebSecurityConfigurer接口定义

    public interface SecurityBuilder<O> {
        /**
         * 构建并返回对象【这里其实就是构建Filter】
         */
        O build() throws Exception;
    }
    
    /*
    * WebSecurityConfigurer接口接受泛型,将使用实现了SecurityBuilder接口的 B 来构造出 O 类型的实例。
    */
    public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
        /**
         * 用户初始化SecurityBuilder类型对象
         */
        void init(B builder) throws Exception;
    
        /**
         * 给SecurityBuilder类型对象设置必要的属性
         */
        void configure(B builder) throws Exception;
    }
    

    小结

    SpringSecurity初始化的意图和主题思想还是相对比较容易理解:

    核心过程
    上图中其实主要做了以下几件事:
    1. WebSecurityConfiguration创建WebSecurity实例
    2. WebSecurity中持有多个SecurityBuilder<SecurityFilterChain>类型实例
    3. WebSecurity迭代执行所有的SecurityBuilder实例得到SecurityFilterChain实例集合
    4. WebSecurityConfiguration调用WebSecurity的build方法将返回的SecurityFilterChain实例集合封装到FilterChainProxy对象中

    可见,上面的过程其实就是为了将各个进行安全处理的Filter创建或收集起来封装到SecurityFilterChain中,再构建出FilterChainProy实例的过程。

    那么如何配置和创建出SecurityFilterChain就是需要真正关注的问题了。在SpringSecurity中定义了SecurityBuilder<O> 接口来构建各种类型的组件,而构建SecurityFilterChain实例的接口则为 SecurityBuilder<? extends SecurityFilterChain>

    SecurityBuilder<SecurityFilterChain> 会被WebSecurity实例调用返回SecurityFilterChain对象,那么WebSecurity又是如何持有SecurityBuilder类型实例对象的引用的呢?其实可以有多种方式来达到这个目的

    1. 执行WebSecurity的方法,将SecurityBuilder添加进去
    2. 在WebSecuriy中直接获取IOC中所有SecurityBuilder类型的实例并添加进来

    除此以外,我们还可以直接将构造好的SecurityFilterChain加入WebSecurity中

    1. 执行WebSecurity的方法,直接将构造好的SecurityFilterChain实例加进去
    2. 在WebSecuriy中直接获取IOC中所有SecurityFilterChain实例添加进来

    我们可以将SecurityConfigurer<O, B>类型实例添加到WebSecurity持有的集合中,在执行期间WebSecurity将自己作为参数执行各个SecurityConfigurer<O, B>的init(B)和configure(B)方法。既然WebSecurity将自己作为参数,那么在SecurityConfigurer<O, B>中不就可以创建好SecurityBuilder<? extends SecurityFilterChain>类型实例并添加进WebSecurity中了(后续要解读的HttpSecurity对象就是SecurityBuilder类型实例)。

    其实WebSecurityConfiguration在运行时就会从IOC容器中获取SecurityConfigurer类型对象并添加到WebSecurity中,如果IOC容器中没有找到,则会使用默认的WebSecurityConfigurerAdapter。

    我们在开发中往往会通过继承WebSecurityConfigurerAdapter来实现自定义的配置,并同时将其加入到IOC容器中,如此一来也就完成了上面的过程。


    以上便是Sprng Security简单的安全配置过程,里面涉及的细节将在后面逐一展开讨论。

    附图:WebSecurity

    https://www.springcloud.cc/spring-security-zhcn.html

    相关文章

      网友评论

        本文标题:Spring Security解析一:安全配置过程概览

        本文链接:https://www.haomeiwen.com/subject/icfhlhtx.html