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