一、基本架构

二、自动配置原理

依据 Spring Boot 自动配置原理,其会自动加载spring-boot-autoconfigure.jar中/META-INF/spring.factories内键值org.springframework.boot.autoconfigure.EnableAutoConfiguration指定的自动配置类。查看该文件,可以看到,与 Spring Security 相关的自动配置类有如下几个:

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,                                      \
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,                            \
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,                                \
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,                             \
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,                   \
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,                               \
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,                               \
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,                    \
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,           \
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,          \
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration, \

每个配置类都为 Spring Security 注入不同的 Bean 到 Spring容器中。这里我们着重介绍一下SecurityFilterAutoConfigurationSecurityAutoConfiguration配置类,因为这两个配置类会自动装配DelegatingFilterProxy和FilterChainProxy到 Spring容器中

三、自动装配FilterChainProxy

下面介绍下配置类SecurityAutoConfiguration,具体如下:

@Configuration(proxyBeanMethods = false)
@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导入了3个配置类

  1. WebSecurityEnablerConfiguration

查看WebSecurityEnablerConfiguration配置类,其源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = "springSecurityFilterChain")
@ConditionalOnClass(EnableWebSecurity.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
class WebSecurityEnablerConfiguration {}

当 Spring容器中没有名称为springSecurityFilterChain的 Bean 等条件时,就会加载该配置类,此时@EnableWebSecurity注解生效

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {boolean debug() default false;
}
  • SpringWebMvcImportSelector的作用是判断当前的环境是否包含springmvc,因为springsecurity可以在非spring环境下使用,为了避免DispatcherServlet的重复配置,所以使用了这个注解来区分。
  • WebSecurityConfiguration顾名思义,是用来配置web安全的,下面的小节会详细介绍。
  • OAuth2ImportSelector:该类是为了对 OAuth2.0 开放授权协议进行支持。ClientRegistration 如果被引用,具体点也就是 spring-security-oauth2 模块被启用(引入依赖jar)时。会启用 OAuth2 客户端配置 OAuth2ClientConfiguration
  • HttpSecurityConfiguration会通过@Autowired去获取容器中的一个AuthenticationManager实例,如果没能获取到则使用依赖注入的AuthenticationConfiguration实例创建一个AuthenticationManager实例,这个实例其实就是ProviderManager。

注解@EnableWebSecurity又导入了4个配置类,这里着重看下

WebSecurityConfiguration:

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {.../*** Creates the Spring Security Filter Chain* @return the {@link Filter} that represents the security filter chain* @throws Exception*/@Bean(name = "springSecurityFilterChain")public Filter springSecurityFilterChain() throws Exception {...return this.webSecurity.build();}...
}

可以看到:

WebSecurityConfiguration#springSecurityFilterChain()最终生成了一个名称为springSecurityFilterChain的Bean 实体,该 Bean 的实际类型其实为FilterChainProxy,是由WebSecurity#build()方法创建的

综上,SecurityAutoConfiguration配置类生成了很多 Bean 实体,其中最重要的一个是名称为springSecurityFilterChain的FilterChainProxy对象

注意:WebSecurityEnablerConfiguration 目的仅仅就是在某些条件下激活 @EnableWebSecurity 注解

  1. SpringBootWebSecurityConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {@Bean@Order(SecurityProperties.BASIC_AUTH_ORDER)SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {//开启所有请求的权限认证(要求表单认证或者httpbasic认证)http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();return http.build();}
}

再看一下@ConditionalOnDefaultWebSecurity

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({DefaultWebSecurityCondition.class})
public @interface ConditionalOnDefaultWebSecurity {}
class DefaultWebSecurityCondition extends AllNestedConditions {DefaultWebSecurityCondition() {super(ConfigurationPhase.REGISTER_BEAN);}@ConditionalOnMissingBean({WebSecurityConfigurerAdapter.class, SecurityFilterChain.class})static class Beans {Beans() {}}@ConditionalOnClass({SecurityFilterChain.class, HttpSecurity.class})static class Classes {Classes() {}}
}

可以看到,当:

  • 条件一 classpath中存在 SecurityFilterChain.class, HttpSecurity.class
  • 条件二 没有自定义 WebSecurityConfigurerAdapter.class, SecurityFilterChain.class

就会引入默认的DefaultWebSecurityCondition。

四、自动装配DelegatingFilterProxy

下面介绍下配置类SecurityFilterAutoConfiguration,其源码如下所示:

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties({SecurityProperties.class})
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
@AutoConfigureAfter({SecurityAutoConfiguration.class})
public class SecurityFilterAutoConfiguration {private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";public SecurityFilterAutoConfiguration() {}@Bean@ConditionalOnBean(name = {"springSecurityFilterChain"})public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);registration.setOrder(securityProperties.getFilter().getOrder());registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));return registration;}private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> {return DispatcherType.valueOf(type.name());}).collect(Collectors.toCollection(() -> {return EnumSet.noneOf(DispatcherType.class);}));}
}

可以看到,要加载SecurityFilterAutoConfiguration前,必须先加载配置类SecurityAutoConfiguration,该配置类前面已经详细介绍了,主要功能就是注入了一个名称为springSecurityFilterChain的 Bean,因此,此时SecurityFilterAutoConfiguration#securityFilterChainRegistration就会生效,最终生成一个DelegatingFilterProxyRegistrationBean实体。DelegatingFilterProxyRegistrationBean实现了ServletContextInitializer接口,当系统执行ServletWebServerApplicationContext.selfInitialize()进行初始化时,会依次调用到:

RegistrationBean.onStartup() --> DynamicRegistrationBean.register()
–> AbstractFilterRegistrationBean.addRegistration()

其中,AbstractFilterRegistrationBean#addRegistration()源码如下:

protected Dynamic addRegistration(String description, ServletContext servletContext) {Filter filter = this.getFilter();return servletContext.addFilter(this.getOrDeduceName(filter), filter);
}

this.getFilter()实际调用的是DelegatingFilterProxyRegistrationBean#getFilter()方法,其内部会创建一个DelegatingFilterProxy实例并返回,源码如下:

public DelegatingFilterProxy getFilter() {return new DelegatingFilterProxy(this.targetBeanName, this.getWebApplicationContext()) {protected void initFilterBean() throws ServletException {}};
}

因此,AbstractFilterRegistrationBean#addRegistration()最终就是通过ServletContext#addFilter将一个DelegatingFilterProxy实例注入到 Servlet 的FilterChain中。

  • DelegatingFilterProxy和SecurityFilterChain的关系

请求进来经由 DelegatingFilterProxy 可以分发到不同的SecurityFilterChain进行授权验证

  • 示意图,当DelegatingFilterProxy注入到FileterChain后,就可以拦截每一个请求,对其做出认证和授权了

五、默认加载的Filter

1. WebAsyncManagerIntegrationFilter 支持集成Spring的异步调用
2. SecurityContextPersistenceFilter 从Session中获取SecurityContext,没有则新建,最终放入SecurityContextHolder中。
3. HeaderWriterFilter 往响应对象response中写入Header属性(Like X-Frame-Options, X-XSS-Protection and X- Content-Type-Options)
4. CsrfFilter csrf校验
5. LogoutFilter 默认拦截[POST]/logout 处理登出逻辑
6. UsernamePasswordAuthenticationFilter 默认拦截[POST]/login 处理登录认证逻辑6.1 第一步 封装Authentication(从request中获取认证信息封装为UsernamePasswordAuthenticationToken)6.2 第二步 获取AuthenticationManager实例[ProviderManager]6.3 第三步 ProviderManager基于委托者模式通过AuthenticationProvider列表完成认证6.4 第四步 认证通过后,将Authentication放入SecurityContextHolder的SecurityContext中。
7. DefaultLoginPageGeneratingFilter 默认拦截[GET]/login 生成登录页面
8. DefaultLogoutPageGeneratingFilter 默认拦截[POST]/login 生成登出页面
9. BasicAuthenticationFilter 认证Basic [Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==]
10. RequestCacheAwareFilter 用于用户登录成功后,重新恢复因为登录被打断的请求
11. SecurityContextHolderAwareRequestFilter 包装request实现servlet api的一些接口方法isUserInRole、getRemoteUser
12. AnonymousAuthenticationFilter 匿名用户认证、信息填充
13. SessionManagementFilter Session管理
14. ExceptionTranslationFilter 异常处理
15. FilterSecurityInterceptor 权限校验、未登录拦截、无权限拦截

spring security加载原理(基于springboot)相关推荐

  1. spring.factories加载原理以及自定义EnvironmentPostProcessor

    目录 spring.factories加载原理 1. SpringApplication的构造方法 1.1 SpringApplication#getSpringFactoriesInstances ...

  2. spring bean加载原理

    简单的分析了一下spring bean的加载原理,属于个人的理解,源码比这个要复杂的多: spring的配置文件applicationContext.xml的内容如下: <?xml versio ...

  3. Spring 配置文件加载原理

    参考:准备Spring Boot的环境 1 核心原理 ⭐️1 在SpringBoot的环境准备阶段的后期, 发布一个ApplicationEnvironmentPreparedEvent事件 ⭐️2 ...

  4. Spring的加载机制导致不同SpringBoot启动方式下@Value注解失效

    问题参考链接:https://blog.csdn.net/u011958281/article/details/81531676 背景: 项目使用application.yml文件自定义参数,我在配置 ...

  5. spring bean的懒加载原理

    spring bean的懒加载原理 1 普通的bean的 初始化是在初始化阶段开始执行的,而被lazy-init修饰的bean则是从容器第一次进行context.getbean("" ...

  6. Alian解读SpringBoot 2.6.0 源码(一):SpringApplication对象创建(Spring工厂加载机制)

    目录 一.背景 二.SpringApplication实例化 2.1.实例化方法入口 2.2.推断应用程序类型 2.3.Spring工厂加载机制 2.3.1.获取Spring工厂实例(重要) 2.3. ...

  7. Spring Security应用详解(集成SpringBoot)

    Spring Security应用详解 集成SpringBoot 工作原理 认证流程 授权流程 集成SpringBoot 1.Spring Boot介绍 Spring Boot是一套Spring的快速 ...

  8. 如何显示Spring Boot加载的所有bean

    在Spring Boot中,您可以使用appContext.getBeanDefinitionNames()来获取Spring容器加载的所有bean. 1. CommandLineRunner作为界面 ...

  9. org.xml.sax.SAXParseException: Failed to read schema document错误的完美解决方法 以及 Spring如何加载XSD文件

    有时候你会发现过去一直启动正常的系统,某天启动时会报出形如下面的错误: org.xml.sax.SAXParseException: schema_reference.4: Failed to rea ...

最新文章

  1. IDEAWebstorm使用
  2. 谷歌浏览器查询缓存视频图片
  3. c语言 链表 删除节点,C语言实现单链表节点的删除(不带头结点)
  4. 导演李大为婚礼全过程(一)
  5. Java IO: Reader And Writer
  6. 【原创】指针和下标的10条对比
  7. java开发技术心得_Java开发核心技术面试心得分析
  8. service程序改为windows窗体展示
  9. Android系列之Fragment(三)----Fragment和Activity之间的通信(含接口回调)
  10. python 异步api ThreadPoolExecutor 、ProcessPoolExecutor(多线程、多进程)
  11. PACKAGE-INFO.JAVA 作用及用法详解
  12. 有道词典笔3新增功能扫读和点读是怎么集成的?
  13. 关于动漫的HTML网页设计作业——动漫网页(刀剑神域6个页面)
  14. PPT打印处理 深色背景/白色字体转换 + 多分页占满
  15. 【最后一天报名】数据科学峰会分论坛:供应链、用户增长、电商零售
  16. GDUT2016年ACM新生杯初赛题解
  17. iOS内购-防越狱破解刷单
  18. 基于视频分析的rPPG心率检测
  19. 阿里云算力的十年更迭史,重点都在这了!
  20. D:\eclipse-workspace\.metadata\.plugins\org.eclipse.core.resources\.projects

热门文章

  1. NYOJ 734 奇数阶魔方
  2. Adnroid提高效率之资源文件改名
  3. Can't connect to MySQL server on 'localhost' 10061
  4. 嵌入式开发基础环境搭建
  5. 协同过滤的itemCF,userCF区别适用场景
  6. Django入门:DoesNotExist: User matching query does not exist.
  7. C与CUDA混合编程的配置问题
  8. 优先队列(priority_queue)的原理及用法
  9. 计算若干数据的汉明距离总和
  10. 【编程】堆(heap)和栈(stack)的区别