概述
当我们使用注解@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;
}
}