WebSecurityConfigurerAdapter自动调用configure(HttpSecurity http)

less 复制代码
@EnableWebSecurity(debug = true)
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(req->req.antMatchers("/api/greeting").authenticated())
        .formLogin(login->login.loginPage("/login")
                .successHandler(jsonLoginSuccessHandler())
        )
        .logout(logout->logout.logoutUrl("/perform_logout"));
    }
}

我们在springSecuriy中经常去一个类来继承WebSecurityConfigurerAdapter,那为什么在 SecurityConfig 中继承了 WebSecurityConfigurerAdapter 并重写了 configure(HttpSecurity http) 方法后,Spring Security 就会自动调用这个方法?

@EnableWebSecurity

less 复制代码
@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;
}

EnableWebSecurity注解导入了配置类,其中有一个是WebSecurityConfiguration 它是整个 Spring Security 自动装配的"调度中心"。

WebSecurityConfiguration

WebSecurityConfiguration中使用autowired和@value的方式将容器中的SecurityConfigurer<Filter, WebSecurity> 类型注入进来,然后遍历每个webSecurityConfigurer 将其应用在webSecurity

less 复制代码
@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));
   ... 省略不重要的代码
   
   for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
      webSecurity.apply(webSecurityConfigurer);
   }
   this.webSecurityConfigurers = webSecurityConfigurers;
}

从容器中找到名为autowiredWebSecurityConfigurersIgnoreParents的bean 调用他的getWebSecurityConfigurers方法获取所有的SecurityConfigurer集合注入进去,底层通过beanFactory获取容器中所有WebSecurityConfigurer类型的bean

typescript 复制代码
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
      ConfigurableListableBeanFactory beanFactory) {
   return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
swift 复制代码
final class AutowiredWebSecurityConfigurersIgnoreParents {

   private final ConfigurableListableBeanFactory beanFactory;

   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<>();
      Map<String, WebSecurityConfigurer> beansOfType = beanFactory
            .getBeansOfType(WebSecurityConfigurer.class);
      for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
         webSecurityConfigurers.add(entry.getValue());
      }
      return webSecurityConfigurers;
   }
}

webSecurity.apply(webSecurityConfigurer);

调用apply方法 底层调用的是webSecurity的add方法,最后将所有的SecurityConfigurer保存进configurers中,它是个map集合

java 复制代码
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
   add(configurer);
   return configurer;
}
swift 复制代码
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
ini 复制代码
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
   Assert.notNull(configurer, "configurer cannot be null");

   Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
         .getClass();
   synchronized (configurers) {
      ...
      List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
            .get(clazz) : null;
      if (configs == null) {
         configs = new ArrayList<>(1);
      }
      configs.add(configurer);
      this.configurers.put(clazz, configs);
      ....
   }
}

springSecurityFilterChain

java 复制代码
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
   ... 省略不重要的代码
   return webSecurity.build();
}

这个方法返回的是整个 Spring Security 的核心过滤器链(Filter),名字叫 springSecurityFilterChain,它会被注册进 Spring 容器,并拦截所有请求。

而它的构建依赖于 webSecurity.build()

springSecurityFilterChain Bean 是整个 Spring Security 的核心过滤器链。

.build() 最终会触发所有配置的 configure() 方法

WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>

在调用 web.build() 时,会进入父类 AbstractConfiguredSecurityBuilder 的逻辑。

ini 复制代码
@Override
protected final O doBuild() throws Exception {
   synchronized (configurers) {
      buildState = BuildState.INITIALIZING;

      beforeInit();
      init();

      buildState = BuildState.CONFIGURING;

      beforeConfigure();
      configure();

      buildState = BuildState.BUILDING;

      O result = performBuild();

      buildState = BuildState.BUILT;

      return result;
   }
}

遍历所有的configure,然后执行他们的init()方法

swift 复制代码
private void init() throws Exception {
   Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

   for (SecurityConfigurer<O, B> configurer : configurers) {
      configurer.init((B) this);
   }

   for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
      configurer.init((B) this);
   }
}
swift 复制代码
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
   List<SecurityConfigurer<O, B>> result = new ArrayList<>();
   for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
      result.addAll(configs);
   }
   return result;
}

遍历所有的configure,然后执行他们的configure()方法,同时将websecurity作为参数传递进去

scss 复制代码
private void configure() throws Exception {
   Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

   for (SecurityConfigurer<O, B> configurer : configurers) {
      configurer.configure((B) this);
   }
}

上面说了build最终会触发所有的configure的init的执行,我们的配置类SecurityConfig继承自WebSecurityConfigurerAdapter,而我们的WebSecurityConfigurerAdapter

scala 复制代码
public class SecurityConfig extends WebSecurityConfigurerAdapter
csharp 复制代码
public abstract class WebSecurityConfigurerAdapter implements
      WebSecurityConfigurer<WebSecurity>
csharp 复制代码
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
      SecurityConfigurer<Filter, T>

也就是我们的配置类实现了SecurityConfigurer接口,那也就是说服务启动的时候会将SecurityConfig放入前面讨论的configures中 等待调用init进行初始化

WebSecurityConfigurerAdapter中定义的init方法会被调用

ini 复制代码
public void init(final WebSecurity web) throws Exception {
   final HttpSecurity http = getHttp();
   web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
      FilterSecurityInterceptor securityInterceptor = http
            .getSharedObject(FilterSecurityInterceptor.class);
      web.securityInterceptor(securityInterceptor);
   });
}

getHttp会被调用,getHttp会创建一个HttpSecurity对象,然后执行

scss 复制代码
protected final HttpSecurity getHttp() throws Exception {
   if (http != null) {
      return http;
   }

   AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
   localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

   AuthenticationManager authenticationManager = authenticationManager();
   authenticationBuilder.parentAuthenticationManager(authenticationManager);
   Map<Class<?>, Object> sharedObjects = createSharedObjects();

   http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
         sharedObjects);
   if (!disableDefaults) {
      // @formatter:off
      http
         .csrf().and()
         .addFilter(new WebAsyncManagerIntegrationFilter())
         .exceptionHandling().and()
         .headers().and()
         .sessionManagement().and()
         .securityContext().and()
         .requestCache().and()
         .anonymous().and()
         .servletApi().and()
         .apply(new DefaultLoginPageConfigurer<>()).and()
         .logout();
      // @formatter:on
      ClassLoader classLoader = this.context.getClassLoader();
      List<AbstractHttpConfigurer> defaultHttpConfigurers =
            SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

      for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
         http.apply(configurer);
      }
   }
   configure(http);
   return http;
}

然后执行 configure(http); 下面的configure方法是WebSecurityConfigurerAdapter中默认实现的,但是我们的配置类继承了WebSecurityConfigurerAdapter,所以最终会调用我们自己的configure方法,以上就是为什么会被调用

scss 复制代码
protected void configure(HttpSecurity http) throws Exception {
   logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

   http
      .authorizeRequests()
         .anyRequest().authenticated()
         .and()
      .formLogin().and()
      .httpBasic();
}

流程总结

SpEL 表达式语言

在 SpEL 中,"@"符号可以用于引用 Spring 容器中的 bean。通过使用"@beanName"的形式,可以在表达式中获取特定 bean 的实例

  1. Bean引用:通过名称引用Spring容器中的bean。

    • #{@myBean}:获取名为myBean的bean实例。

在 SpEL(Spring Expression Language)中,@ 用来引用 Spring 容器中的 Bean。

@ 在 SpEL 中的含义

  • @beanName:表示从 Spring IoC 容器中获取名为 beanName 的 Bean 实例。
  • 它等价于调用 ApplicationContext.getBean("beanName")

所以:

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)

意思是:

"请从 Spring 容器中找到一个名字叫 autowiredWebSecurityConfigurersIgnoreParents 的 Bean。"

  • 当 Spring 解析
ruby 复制代码
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
    1.  找到 `autowiredWebSecurityConfigurersIgnoreParents` 这个 Bean。
    2.  调用其 `getWebSecurityConfigurers()` 方法。
    3.  将返回的 `List<SecurityConfigurer<...>>` 注入到当前方法的参数中。

⚠️ 注意:WebSAdapterSpring Security 5ecurityConfigurer.7+ 已被标记为过时

相关推荐
天机️灵韵1 小时前
开源医院信息管理系统:基于若依框架的智慧医疗解决方案
java·开发语言·spring boot·spring cloud·github·开源项目
Resean02232 小时前
SpringMVC 6+源码分析(三)DispatcherServlet实例化流程 2--(url 与contrller类如何进行映射)
java·spring boot·spring
MacroZheng3 小时前
狂揽9.3k star!号称终端版Postman项目,太炫酷了!
java·spring boot·后端
都叫我大帅哥4 小时前
全面深入解析Hystrix:Java分布式系统的"防弹衣" 🛡️
java·spring boot·spring cloud
椰椰椰耶6 小时前
【Spring】SpringBoot自动注入原理分析,@SpringBootApplication、@EnableAutoConfiguration详解
java·spring boot·spring
2301_7930868710 小时前
Springboot 04 starter
java·spring boot·后端
太阳伞下的阿呆13 小时前
本地环境vue与springboot联调
前端·vue.js·spring boot
飞翔的佩奇13 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的名城小区物业管理系统(附源码+数据库+毕业论文+开题报告+部署教程+配套软件)
数据库·vue.js·spring boot·mysql·毕业设计·mybatis·小区物业管理系统
Pitayafruit14 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
spring boot·后端·ai编程