我们可以通过 org.springframework.boot.autoconfigure.security.servlet
路径下找到 Spring Security 关于Servlet
的自动配置类。我们来大致了解一下。
package org.springframework.boot.autoconfigure.security.servlet;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityDataConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Madhura Bhave
* @since 1.0.0
*/
@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
SecurityAutoConfiguration
顾名思义安全配置类。该类引入(@import
)了 SpringBootWebSecurityConfiguration
、WebSecurityEnablerConfiguration
和 SecurityDataConfiguration
三个配置类。 让这三个模块的类生效。是一个复合配置,是 Spring Security 自动配置最重要的一个类之一。 Spring Boot 自动配置经常使用这种方式以达到灵活配置的目的,这也是我们研究 Spring Security 自动配置的一个重要入口 同时 SecurityAutoConfiguration
还将 DefaultAuthenticationEventPublisher
作为默认的 AuthenticationEventPublisher
注入 Spring IoC 容器。如果你熟悉 Spring 中的事件机制你就会知道该类是一个 Spring 事件发布器。该类内置了一个HashMap
>
维护了认证异常处理和对应异常事件处理逻辑的映射关系,比如账户过期异常 AccountExpiredException
对应认证过期事件AuthenticationFailureExpiredEvent
,也就是说发生不同认证的异常使用不同处理策略。
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}
这个类是Spring Security 对 Spring Boot Servlet Web 应用的默认配置。核心在于WebSecurityConfigurerAdapter
适配器。从 @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
我们就能看出 WebSecurityConfigurerAdapter
是安全配置的核心。 默认情况下 DefaultConfigurerAdapter
将以SecurityProperties.BASIC_AUTH_ORDER
(-5
) 的顺序注入 Spring IoC 容器,这是个空实现。如果我们需要个性化可以通过继承 WebSecurityConfigurerAdapter
来实现。我们会在以后的博文重点介绍该类。
@Configuration
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
}
该配置类会在SpringBootWebSecurityConfiguration
注入 Spring IoC 容器后启用 @EnableWebSecurity
注解。也就是说 WebSecurityEnablerConfiguration
目的仅仅就是在某些条件下激活 @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;
}
@Enable*
这类注解都是带配置导入的注解。通过导入一些配置来启用一些特定功能。 @EnableWebSecurity
导入了 WebSecurityConfiguration
、SpringWebMvcImportSelector
、OAuth2ImportSelector
以及启用了 @EnableGlobalAuthentication
注解。
该配置类WebSecurityConfiguration
使用一个WebSecurity
对象基于用户指定的或者默认的安全配置,你可以通过继承 WebSecurityConfigurerAdapter
或者实现 WebSecurityConfigurer
来定制 WebSecurity
创建一个FilterChainProxy
Bean来对用户请求进行安全过滤。这个FilterChainProxy
的名称就是 WebSecurityEnablerConfiguration
上的 BeanIds.SPRING_SECURITY_FILTER_CHAIN
也就是 springSecurityFilterChain
,它是一个Filter,最终会被作为Servlet过滤器链中的一个Filter应用到Servlet容器中。安全处理的策略主要是过滤器的调用顺序。WebSecurityConfiguration
最终会通过 @EnableWebSecurity
应用到系统。
源码分析:
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* Spring Web Security 的配置类 :
* 1. 使用一个 WebSecurity 对象基于安全配置创建一个 FilterChainProxy 对象来对用户请求进行安全过滤。
* 2. 也会暴露诸如 安全SpEL表达式处理器 SecurityExpressionHandler 等一些类。
*
* @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;
/**
*
* 代理监听器 应该时监听 DefaultAuthenticationEventPublisher 的一些处理策略
*/
@Bean
public static DelegatingApplicationListener delegatingApplicationListener() {
return new DelegatingApplicationListener();
}
/**
*
* 安全SpEL表达式处理器 SecurityExpressionHandler 缺省为一个 DefaultWebSecurityExpressionHandler
*/
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
return webSecurity.getExpressionHandler();
}
/**
* Spring Security 核心过滤器 Spring Security Filter Chain , Bean ID 为 springSecurityFilterChain
* @return the {@link Filter} that represents the security filter chain
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
/**
*
* 用于模板 如JSP Freemarker 的一些页面标签按钮控制支持
* 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();
}
/**
*
* 用于创建web configuration的SecurityConfigurer实例,
* 注意该参数通过@Value(...)方式注入,对应的bean autowiredWebSecurityConfigurersIgnoreParents
* 也在该类中定义
*
* @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 {
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容器中获取所有的WebSecurityConfigurer bean。
* 这些WebSecurityConfigurer通常是由开发人员实现的配置类,并且继承自WebSecurityConfigurerAdapter
*
*/
@Bean
public static 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;
}
}
/*
* 要是为了获取注解 @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);
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.
* lang.ClassLoader)
*/
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
}
该类是为了对 Spring Mvc 进行支持的。一旦发现应用使用 Spring Mvc 的核心前置控制器 DispatcherServlet
就会引入 WebMvcSecurityConfiguration
。主要是为了适配 Spring Mvc 。
该类是为了对 OAuth2.0
开放授权协议进行支持。ClientRegistration
如果被引用,具体点也就是 spring-security-oauth2
模块被启用(引入依赖jar)时。会启用 OAuth2
客户端配置 OAuth2ClientConfiguration
。
这个类主要引入了 AuthenticationConfiguration
目的主要为了构造 认证管理器 AuthenticationManager
。AuthenticationManager
十分重要后面我们会进行专门的分析。
我们在 org.springframework.boot.autoconfigure.security.servlet
路径下还发现了一个配置类 SecurityFilterAutoConfiguration
。该类用于向Servlet容器注册一个名称为securityFilterChainRegistration
的bean, 实现类是DelegatingFilterProxyRegistrationBean
。该 bean 的目的是注册另外一个 Servlet Filter Bean
到 Servlet
容器,实现类为 DelegatingFilterProxy
。DelegatingFilterProxy
其实是一个代理过滤器,它被 Servlet
容器用于处理请求时,会将任务委托给指定给自己另外一个Filter bean。对于 SecurityFilterAutoConfiguration
,来讲,这个被代理的Filter bean的名字为 springSecurityFilterChain
, 也就是我们上面提到过的 Spring Security Web提供的用于请求安全处理的Filter bean,其实现类是 FilterChainProxy
。
相关的源码分析:
package org.springframework.boot.autoconfigure.security.servlet;
import java.util.EnumSet;
import java.util.stream.Collectors;
import javax.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
@Configuration
// 仅在 Servlet 环境下生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 确保安全属性配置信息被加载并以bean形式被注册到容器
@EnableConfigurationProperties(SecurityProperties.class)
// 仅在特定类存在于 classpath 上时才生效
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class,
SessionCreationPolicy.class })
// 指定该配置类在 SecurityAutoConfiguration 配置类应用之后应用
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
// 要注册到 Servlet 容器的 DelegatingFilterProxy Filter的
// 目标代理Filter bean的名称 :springSecurityFilterChain
private static final String DEFAULT_FILTER_NAME =
AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
// 定义一个 bean securityFilterChainRegistration,
// 该 bean 的目的是注册另外一个 bean 到 Servlet 容器 : 实现类为 DelegatingFilterProxy 的一个 Servlet Filter
// 该 DelegatingFilterProxy Filter 其实是一个代理过滤器,它被 Servlet 容器用于匹配特定URL模式的请求,
// 而它会将任务委托给指定给自己的名字为 springSecurityFilterChain 的 Filter, 也就是 Spring Security Web
// 提供的用于请求安全处理的一个 Filter bean,其实现类是 FilterChainProxy
// (可以将 1 个 FilterChainProxy 理解为 1 HttpFirewall + n SecurityFilterChain)
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(
SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
}
本文主要对 Spring Security 在 Spring Boot 中的自动配置一些机制进行了粗略的讲解。为什么没有细讲。因为从学习出发有些东西不是我们必须要深入了解的,但是又要知道一点点相关的知识。我们先宏观上有个大致的了解就行。所以在阅读本文一定不要钻牛角尖。粗略知道配置策略、加载策略和一些关键类的作用即可。在你对 Spring Security 有了进一步学习之后,回头认真来看这些配置类会有更深层的思考。 从另一个方面该文也给你阅读 Spring 源码提供了一些思路,学会这些才是最重要的。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
添加我为好友,拉您入交流群!
请使用微信扫一扫!