diff --git a/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java index f89e06ca79..29b4e7be14 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java +++ b/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ * @param The object that this builder returns * @param The type of this builder (that is returned by the base class) * @author Rob Winch + * @author DingHao * @see WebSecurity */ public abstract class AbstractConfiguredSecurityBuilder> @@ -59,7 +60,7 @@ public abstract class AbstractConfiguredSecurityBuilder>, List>> configurers = new LinkedHashMap<>(); - private final List> configurersAddedInInitializing = new ArrayList<>(); + private List> configurersAddedInInitializing = new ArrayList<>(); private final Map, Object> sharedObjects = new HashMap<>(); @@ -386,8 +387,12 @@ private void init() throws Exception { for (SecurityConfigurer configurer : configurers) { configurer.init((B) this); } - for (SecurityConfigurer configurer : this.configurersAddedInInitializing) { - configurer.init((B) this); + while (!this.configurersAddedInInitializing.isEmpty()) { + List> toInit = this.configurersAddedInInitializing; + this.configurersAddedInInitializing = new ArrayList<>(); + for (SecurityConfigurer configurer : toInit) { + configurer.init((B) this); + } } } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java index 322459e33a..283637bcb7 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -163,6 +163,36 @@ public void withWhenDuplicateConfigurerAddedThenDuplicateConfigurerRemoved() thr assertThat(this.builder.getConfigurers(TestSecurityConfigurer.class)).hasSize(1); } + @Test + public void withWhenConfigurerAddInitializing() throws Exception { + this.builder.with(new AppliesNestedConfigurer(), Customizer.withDefaults()); + assertThat(this.builder.build()).isEqualTo("success"); + } + + private static class AppliesNestedConfigurer + extends SecurityConfigurerAdapter { + + @Override + public void init(TestConfiguredSecurityBuilder builder) throws Exception { + builder.with(new NestedConfigurer(), Customizer.withDefaults()); + } + + } + + private static class NestedConfigurer extends SecurityConfigurerAdapter { + + @Override + public void init(TestConfiguredSecurityBuilder http) throws Exception { + http.with(new DoubleNestedConfigurer(), Customizer.withDefaults()); + } + + } + + private static class DoubleNestedConfigurer + extends SecurityConfigurerAdapter { + + } + private static class ApplyAndRemoveSecurityConfigurer extends SecurityConfigurerAdapter {