为什么引入springsecurity的依赖后,会自动创建了过滤器链

spring.factories.imports 文件

在 Spring Boot 2.7 之前,自动配置类的注册是通过 META-INF/spring.factories 文件完成的,其中包含类似这样的条目:

为了提高性能、模块化和可读性,Spring Boot 2.7 引入了新的、专用的 .imports 文件机制来替代 spring.factories 中的自动配置注册。

org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件是一个纯文本文件,每行包含一个自动配置类的全限定类名(Fully Qualified Class Name)

  • 每行一个类名
  • 支持注释 :以 # 开头的行被视为注释,可以用来说明或临时禁用某个配置。
  • 语义清晰:文件名本身就说明了它的用途------注册自动配置导入。

工作原理

  1. 启动扫描 :当你的 Spring Boot 应用程序启动时,SpringApplication 会扫描所有 classpath 下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。
  2. 读取类名:它会读取这些文件中的每一行,获取自动配置类的全限定名。
  3. 条件化加载 :对于每个列出的类,Spring Boot 会检查其上的条件注解(如 @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty 等)。
  4. 注册 Bean :如果所有条件都满足,该自动配置类就会被加载,其中定义的 @Bean 方法会被执行,相关的组件(如数据源、模板引擎、安全配置等)就会自动注册到 Spring 应用上下文中。

SecurityAutoConfiguration

当我们引入了security相关依赖的时候,SecurityAutoConfiguration自动配置类开始发挥作用

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
ruby 复制代码
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {

}

SecurityAutoConfiguration中引入了SpringBootWebSecurityConfiguration配置类

less 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

  
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnDefaultWebSecurity
   static class SecurityFilterChainConfiguration {
        
      //配置默认的过滤器链条  
      @Bean
      @Order(SecurityProperties.BASIC_AUTH_ORDER)
      SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
         http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
         http.formLogin(withDefaults());
         http.httpBasic(withDefaults());
         return http.build();
      }

   }

   // 启用@EnableWebSecurity注解
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
   @ConditionalOnClass(EnableWebSecurity.class)
   @EnableWebSecurity
   static class WebSecurityEnablerConfiguration {

   }

}

SpringBootWebSecurityConfiguration做了两件事相对重要 一是创建了默认的过滤器链,二是使用了@EnableWebSecurity注解 这就是为什么我们只引入了jar包 springsecurity就可以通过默认的过滤器链来保护我们的资源,同时我们也可以看出开发者如果忘记了使用@EnableWebSecurity注解也没关系,框架会自动帮我们开启。

默认过滤器链

从上面的代码我们可以看出 SecurityFilterChainConfiguration将SecurityFilterChain实例注入到容器中,但前提是@ConditionalOnDefaultWebSecurity这个注解需要生效

css 复制代码
@ConditionalOnDefaultWebSecurity

@ConditionalOnDefaultWebSecurity本质上是@Conditional注解

less 复制代码
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(DefaultWebSecurityCondition.class)
public @interface ConditionalOnDefaultWebSecurity {

}

@Conditional(DefaultWebSecurityCondition.class) 这个是spring的条件化配置,意思是只有当 DefaultWebSecurityCondition 这个条件被判定为 true 时,被它标注的类或方法才应该被注册到 Spring 容器中(即生效);否则,就跳过它。

@Conditional注解要求传入一个实现了Condition接口的类作为参数,DefaultWebSecurityCondition实现了这个接口

  • 在 Spring 应用上下文启动过程中,当遇到被 @Conditional 标注的元素时,Spring 会实例化你指定的 Condition 类(这里是 DefaultWebSecurityCondition),并调用其 matches() 方法。
java 复制代码
@FunctionalInterface
public interface Condition {
   boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

这个类的主要作用是: 当内部类的所有的 .@Conditional 注解都成立的时候,才会注入该 Bean

scala 复制代码
class DefaultWebSecurityCondition extends AllNestedConditions {

   DefaultWebSecurityCondition() {
      super(ConfigurationPhase.REGISTER_BEAN);
   }

   //  **作用**:`@ConditionalOnClass` 注解检查类路径(classpath)上是否存在指定的类。
   //**逻辑**:只有当 `SecurityFilterChain.class` 和 `HttpSecurity.class` 这两个类**都存在于**类路径上时,这个条件才算通过
   @ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
   static class Classes {

   }

   // 只有当当前 Spring 容器中**没有**类型为 `SecurityFilterChain` 的 Bean 时,这个条件才算通过
   @ConditionalOnMissingBean({ SecurityFilterChain.class })
   static class Beans {

   }

}

所以springsecurity自动创建过滤器链的满足的条件是:1 类需要存在 2.用户没有手动的配置SecurityFilterChain。

目的 :这是实现"自定义优先 "(Opinionated Defaults)的关键。Spring Boot 的理念是,如果开发者没有进行任何安全配置,它就提供一个安全的默认配置(通常是一个简单的登录表单)。但如果开发者自己定义了一个 SecurityFilterChain Bean 来配置自定义的安全规则(比如 OAuth2、JWT、特定的 URL 权限等),那么 Spring Boot 就应该自动禁用 其默认的安全配置,避免冲突。这个条件确保了只有在用户没有提供自己的安全配置时,才会应用默认配置

EnableWebSecurity

上面说自动配置类WebSecurityEnablerConfiguration帮我开启EnableWebSecurity注解, 但是有个前提是

@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN) @ConditionalOnClass(EnableWebSecurity.class) 这两个条件得同时生效 1是类路径中要存在EnableWebSecurity这个类 2是** Spring 容器中不存在名为 "springSecurityFilterChain" 的 Bean**

这么做的目的是:当 @EnableWebSecurity 注解被处理时,它会创建一个名为 springSecurityFilterChainFilterChainProxy Bean,这个 Bean 是整个 Spring Security 过滤器链的入口,如果用户正确地使用了 @EnableWebSecurity @EnableWebSecurity 注解本身就会触发创建 springSecurityFilterChain Bean。 同样,WebSecurityEnablerConfiguration 会因为该 Bean 已存在而被禁用,防止了 @EnableWebSecurity 被应用两次。

相关推荐
IT_陈寒26 分钟前
SpringBoot 3.x实战:5种高并发场景下的性能优化秘籍,让你的应用快如闪电!
前端·人工智能·后端
Victor35643 分钟前
Redis(47)如何配置Redis哨兵?
后端
Victor35644 分钟前
Redis(46) 如何搭建Redis哨兵?
后端
尘鹄6 小时前
go 初始化组件最佳实践
后端·设计模式·golang
墩墩分墩6 小时前
【Go语言入门教程】 Go语言的起源与技术特点:从诞生到现代编程利器(一)
开发语言·后端·golang·go
一个松7 小时前
【无标题】
spring boot
程序员爱钓鱼8 小时前
Go语言实战案例- 开发一个ToDo命令行工具
后端·google·go
大怪v9 小时前
老乡,别走!Javascript隐藏功能你知道吗?
前端·javascript·代码规范
学渣676569 小时前
文件传输工具rsync|rust开发环境安装|Ascend实验相关命令
开发语言·后端·rust
我是渣哥9 小时前
Java String vs StringBuilder vs StringBuffer:一个性能优化的探险故事
java·开发语言·jvm·后端·算法·职场和发展·性能优化