1. 前言
我们在前几篇对 Spring Security 的用户信息管理机制,密码机制进行了探讨。我们发现
Spring Security Starter
相关的
Servlet
自动配置都在
spring-boot-autoconfigure-2.1.9.RELEASE
(当前 Spring Boot 版本为
2.1.9.RELEASE
) 模块的路径
org.springframework.boot.autoconfigure.security.servlet
之下。其实官方提供的Starter组件的自动配置你都能在
spring-boot-autoconfigure-2.1.9.RELEASE
下找到。今天我们进一步来解密
Spring Security
在
Spring Boot
的配置和使用。
2. Spring Boot 下 Spring Security 的自动配置
我们可以通过
org.springframework.boot.autoconfigure.security.servlet
路径下找到
Spring Security
关于
Servlet
的自动配置类。我们来大致了解一下。
2.1 SecurityAutoConfiguration
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<String, Constructor<? extends AbstractAuthenticationEvent>>
维护了认证异常处理和对应异常事件处理逻辑的映射关系,比如账户过期异常
AccountExpiredException
对应认证过期事件
AuthenticationFailureExpiredEvent
,也就是说发生不同认证的异常使用不同处理策略。
2.2 SpringBootWebSecurityConfiguration
@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
来实现。我们会在以后的博文重点介绍该类。
2.3 WebSecurityEnablerConfiguration
@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
注解。那么这个注解都有什么呢?
3. @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
注解。
3.1 WebSecurityConfiguration
该配置类
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