天天看点

Spring Security Config : WebSecurityConfiguration Web 安全配置概述源代码分析

概述

当我们使用注解

@EnableWebSecurity

启用

Spring Security

时,其实导入了一个配置类

WebSecurityConfiguration

,如下所示:

@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.class })
@Configuration
public @interface EnableWebSecurity {
	boolean debug() default false;
}
           

该配置类

WebSecurityConfiguration

使用一个

WebSecurity

对象基于用户指定的或者默认的安全配置,创建一个

FilterChainProxy bean

来对用户请求进行安全过滤。这个

FilterChainProxy bean

的名称为

springSecurityFilterChain

,它也是一个

Filter

,最终会被作为

Servlet

过滤器链中的一个

Filter

应用到

Servlet

容器中。

你可以认为

WebSecurityConfiguration

Spring Web

安全过滤器

springSecurityFilterChain

的提供方。至于该安全过滤器如何被获取和使用,我们在其他文章中分析。

这里的安全配置可能来自

XML

配置,也可能来自

Java

配置类。在基于

Springboot

web

应用中,通常基于

WebSecurityConfigurerAdapter

的某个子类,该子类由开发人员实现并带上注解

@Configuration

,用于进行定制安全配置。

源代码版本 Spring Security Config 5.1.2.RELEASE

源代码分析

package org.springframework.security.config.annotation.web.configuration;

// 忽略 import 行


/**
* Spring Web Security 的配置类 : 
*  1. 使用一个 WebSecurity 对象基于安全配置创建一个 FilterChainProxy 对象来对用户请求进行安全过滤。 
*  2. 也会暴露一些必要的 bean。
*  3. 如何定制 Spring security 的web 安全,也就是 WebSecurity 对象 ?
*     3.1 实现一个继承自 WebSecurityConfigurerAdapter 的配置类 , 
*     3.2 或者 提供一个配置类,实现了接口 WebSecurityConfigurer
*    该配置类的配置会在使用 @EnableWebSecurity 时应用到系统。
*
* Uses a WebSecurity to create the FilterChainProxy that performs the web based security 
* for Spring Security. It then exports the necessary beans. Customizations can be made to 
* WebSecurity by extending WebSecurityConfigurerAdapter and exposing it as a Configuration 
* or implementing WebSecurityConfigurer and exposing it as a Configuration. This configuration 
* is imported when using EnableWebSecurity.
*
* @see EnableWebSecurity
* @see WebSecurity
*
* @author Rob Winch
* @author Keesun Baik
* @since 3.2
*/
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    private WebSecurity webSecurity;

    // 是否启用了调试模式,来自注解 @EnableWebSecurity 的属性 debug,缺省值 false
    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,是表达式处理器,缺省为一个 DefaultWebSecurityExpressionHandler,
    //  仅在 bean springSecurityFilterChain 实例化之后才能实例化
    @Bean
    @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
        return webSecurity.getExpressionHandler();
    }

    /**
     * Creates the Spring Security Filter Chain 
     * 定义 Spring Security Filter Chain  , 名字为 springSecurityFilterChain
     * @return
     * @throws Exception
     */
    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        boolean hasConfigurers = webSecurityConfigurers != null
                && !webSecurityConfigurers.isEmpty();
        if (!hasConfigurers) {
            // 如果没有 webSecurityConfigurer, 则提供一个却省的
            WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                    .postProcess(new WebSecurityConfigurerAdapter() {
                    });
            webSecurity.apply(adapter);
        }
        
        // 根据配置 webSecurityConfigurers或者缺省 WebSecurityConfigurerAdapter 构建 
        // Filter FilterChainProxy 并返回之,这是最终加入到Servlet容器的Filter chain
        // 中的一个 Filter, 但实际上,它的内部也维护了一个自己的安全相关的 Filter chain。
        return webSecurity.build();
    }

     // 定义一个bean,是web调用权限评估器,用于判断一个用户是否可以访问某个URL,
    // 对于 JSP tag 支持必要。 仅在bean springSecurityFilterChain 被定义时才生效。
    /**
     * Creates the WebInvocationPrivilegeEvaluator that is necessary for the JSP
     * tag support.
     * @return the WebInvocationPrivilegeEvaluator
     * @throws Exception
     */
    @Bean
    @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception {
        return webSecurity.getPrivilegeEvaluator();
    }

    /**
     * Sets the <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>
     * instances used to create the web configuration.
     *
     * @param objectPostProcessor the ObjectPostProcessor used to create a
     * WebSecurity instance
     * @param webSecurityConfigurers the
     * <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder> instances used to
     * create the web configuration 用于创建web configuration的SecurityConfigurer实例,
     * 注意该参数通过@Value(...)方式注入,对应的bean autowiredWebSecurityConfigurersIgnoreParents
     * 也在该类中定义
     * @throws Exception
     */
    @Autowired(required = false)
    public void setFilterChainProxySecurityConfigurer(
            ObjectPostProcessor<Object> objectPostProcessor,
            @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") 
            List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
            throws Exception {
        webSecurity = objectPostProcessor
                .postProcess(new WebSecurity(objectPostProcessor));
        if (debugEnabled != null) {
            webSecurity.debug(debugEnabled);
        }

        Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

        Integer previousOrder = null;
        Object previousConfig = null;
        for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
            Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
            if (previousOrder != null && previousOrder.equals(order)) {
                throw new IllegalStateException(
                        "@Order on WebSecurityConfigurers must be unique. Order of "
                                + order + " was already used on " + previousConfig + ", so it cannot be used on "
                                + config + " too.");
            }
            previousOrder = order;
            previousConfig = config;
        }
        for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
            webSecurity.apply(webSecurityConfigurer);
        }
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

	// 定义一个bean,类型为AutowiredWebSecurityConfigurersIgnoreParents,其作用为从Spring容器中
	// 获取所有类型为WebSecurityConfigurer的bean,这些bean就是要应用的安全配置原料
    @Bean
    public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
            ConfigurableListableBeanFactory beanFactory) {
        return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
    }

    /**
     * A custom verision of the Spring provided AnnotationAwareOrderComparator that uses
     * AnnotationUtils#findAnnotation(Class, Class) to look on super class
     * instances for the 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;
        }
    }

    /*
     * 获取导入该配置bean的配置bean上的注解元数据并设置到该配置bean
     *  这里主要是为了获取注解 @EnableWebSecurity 的属性 debugEnabled
     *
     * @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);
        }
    }

    /*
     *
     *
     * @see
     * org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.
     * lang.ClassLoader)
     */
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }
}

           

AutowiredWebSecurityConfigurersIgnoreParents

这是一个工具类,从当前

bean

容器中获取所有的

WebSecurityConfigurer bean

。这些

WebSecurityConfigurer

通常是由开发人员实现的配置类,并且继承自

WebSecurityConfigurerAdapter

上面引用到的类

AutowiredWebSecurityConfigurersIgnoreParents

:

/**
 * 一个工具类,从当前bean容器中获取所有的WebSecurityConfigurer实例。
 *
 * @author Rob Winch
 *
 */
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;
	}
}
           

继续阅读