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+ 已被标记为过时

相关推荐
贫民窟的勇敢爷们3 小时前
SpringBoot整合AOP切面编程实战,实现日志统一记录+接口权限校验
java·spring boot·spring
吾疾唯君医7 小时前
Java SpringBoot集成积木报表实操记录
java·spring boot·spring·导出excel·积木报表·数据文件下载
正儿八经的少年10 小时前
Spring Boot 两种激活配置方式的作用与区别
java·spring boot·后端
疯狂成瘾者11 小时前
Spring Boot 项目中的 SMTP 邮件验证码服务技术解析
java·spring boot·后端
啃臭12 小时前
AOP和反射
java·spring boot
河阿里12 小时前
SpringBoot:Spring Task定时任务完整使用教学
java·spring boot·spring
五阿哥永琪15 小时前
从0开始做一个导出功能,完整流程
spring boot
java1234_小锋16 小时前
SpringBoot可以同时处理多少请求?
java·spring boot·后端
海棠Flower未眠16 小时前
Spring Boot 3 + JPA多模块系统对MySQL和DORIS进行多数据源集成实战(荣耀典藏版)
spring boot·后端·mysql
北风朝向17 小时前
Spring Boot 集成 Open WebUI 实现 AI 流式对话
人工智能·spring boot·状态模式