diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index 4313d68e3a..1c2fd4efcc 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java @@ -29,6 +29,7 @@ import org.springframework.security.config.annotation.AbstractConfiguredSecurity import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.SecurityBuilder; import org.springframework.security.config.annotation.SecurityConfigurer; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; @@ -111,6 +112,7 @@ import org.springframework.util.Assert; public final class HttpSecurity extends AbstractConfiguredSecurityBuilder implements SecurityBuilder, HttpSecurityBuilder { private AuthenticationManager authenticationManager; + private final RequestMatcherConfigurer requestMatcherConfigurer = new RequestMatcherConfigurer(); private List filters = new ArrayList(); private RequestMatcher requestMatcher = new AnyRequestMatcher(); private FilterComparator comparitor = new FilterComparator(); @@ -132,8 +134,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilderExample Configurations * @@ -235,12 +236,11 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder openidLogin() throws Exception { - return apply(new OpenIDLoginConfigurer()); + return getOrApply(new OpenIDLoginConfigurer()); } /** - * Allows configuring of Session Management. Multiple invocations of - * {@link #sessionManagement()} will override previous invocations. + * Allows configuring of Session Management. * *

Example Configuration

* @@ -303,7 +303,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder sessionManagement() throws Exception { - return apply(new SessionManagementConfigurer()); + return getOrApply(new SessionManagementConfigurer()); } /** @@ -358,7 +358,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder portMapper() throws Exception { - return apply(new PortMapperConfigurer()); + return getOrApply(new PortMapperConfigurer()); } /** @@ -434,7 +434,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder jee() throws Exception { - return apply(new JeeConfigurer()); + return getOrApply(new JeeConfigurer()); } /** @@ -467,12 +467,11 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder x509() throws Exception { - return apply(new X509Configurer()); + return getOrApply(new X509Configurer()); } /** - * Allows configuring of Remember Me authentication. Multiple invocations of - * {@link #rememberMe()} will override previous invocations. + * Allows configuring of Remember Me authentication. * *

Example Configuration

* @@ -514,15 +513,12 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder rememberMe() throws Exception { - return apply(new RememberMeConfigurer()); + return getOrApply(new RememberMeConfigurer()); } /** * Allows restricting access based upon the {@link HttpServletRequest} using - * {@link RequestMatcher} implementations (i.e. via URL patterns). Invoking - * {@link #authorizeUrls()} twice will override previous invocations of - * {@link #authorizeUrls()}. * *

Example Configurations

* @@ -611,7 +607,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder authorizeUrls() throws Exception { - return apply(new ExpressionUrlAuthorizationConfigurer()); + return getOrApply(new ExpressionUrlAuthorizationConfigurer()); } /** @@ -624,7 +620,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder requestCache() throws Exception { - return apply(new RequestCacheConfigurer()); + return getOrApply(new RequestCacheConfigurer()); } /** @@ -635,7 +631,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder exceptionHandling() throws Exception { - return apply(new ExceptionHandlingConfigurer()); + return getOrApply(new ExceptionHandlingConfigurer()); } /** @@ -647,7 +643,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder securityContext() throws Exception { - return apply(new SecurityContextConfigurer()); + return getOrApply(new SecurityContextConfigurer()); } /** @@ -659,7 +655,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder servletApi() throws Exception { - return apply(new ServletApiConfigurer()); + return getOrApply(new ServletApiConfigurer()); } /** @@ -715,7 +711,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder logout() throws Exception { - return apply(new LogoutConfigurer()); + return getOrApply(new LogoutConfigurer()); } /** @@ -796,7 +792,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder anonymous() throws Exception { - return apply(new AnonymousConfigurer()); + return getOrApply(new AnonymousConfigurer()); } /** @@ -876,13 +872,12 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder formLogin() throws Exception { - return apply(new FormLoginConfigurer()); + return getOrApply(new FormLoginConfigurer()); } /** * Configures channel security. In order for this configuration to be useful at least - * one mapping to a required channel must be provided. Invoking this method multiple times - * will reset previous invocations of the method. + * one mapping to a required channel must be provided. * *

Example Configuration

* @@ -925,12 +920,11 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder requiresChannel() throws Exception { - return apply(new ChannelSecurityConfigurer()); + return getOrApply(new ChannelSecurityConfigurer()); } /** - * Configures HTTP Basic authentication. Multiple infocations of - * {@link #httpBasic()} will override previous invocations. + * Configures HTTP Basic authentication. * *

Example Configuration

* @@ -968,7 +962,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder httpBasic() throws Exception { - return apply(new HttpBasicConfigurer()); + return getOrApply(new HttpBasicConfigurer()); } @Override @@ -1115,9 +1109,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder * - * The configuration differs from the previous configurations because it invokes - * {@link #requestMatchers()} twice which resets the {@link RequestMatcherConfigurer}. - * Therefore the configuration below only matches on URLs that start with "/oauth/**". + * The configuration below is also the same as the above configuration. * *
      * @Configuration
@@ -1153,7 +1145,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder> C getOrApply(C configurer)
+            throws Exception {
+        C existingConfig = (C) getConfigurer(configurer.getClass());
+        if(existingConfig != null) {
+            return existingConfig;
+        }
+        return apply(configurer);
+    }
+
     /**
      * Internal {@link RequestMatcher} instance used by {@link RequestMatcher}
      * that will match if any of the passed in {@link RequestMatcher} instances
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.groovy
new file mode 100644
index 0000000000..3966efb3e4
--- /dev/null
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2002-2013 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.annotation.web.configurers
+
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.config.annotation.AnyObjectPostProcessor
+import org.springframework.security.config.annotation.BaseSpringSpec
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+import org.springframework.security.web.authentication.logout.LogoutFilter
+
+/**
+ *
+ * @author Rob Winch
+ */
+class AnonymousConfigurerTests extends BaseSpringSpec {
+
+    def "invoke logout twice does not override"() {
+        when:
+            loadConfig(InvokeTwiceDoesNotOverride)
+        then:
+            findFilter(AnonymousAuthenticationFilter).key == "custom"
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .anonymous()
+                    .key("custom")
+                    .and()
+                .anonymous()
+        }
+    }
+}
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurerTests.groovy
index e46c22eca8..af5c945ba2 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurerTests.groovy
@@ -15,10 +15,13 @@
  */
 package org.springframework.security.config.annotation.web.configurers
 
+import org.springframework.context.annotation.Configuration
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
 import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl
 import org.springframework.security.web.access.channel.ChannelProcessingFilter
 import org.springframework.security.web.access.channel.InsecureChannelProcessor
@@ -50,4 +53,27 @@ class ChannelSecurityConfigurerTests extends BaseSpringSpec {
         and: "ChannelProcessingFilter is registered with LifecycleManager"
             1 * objectPostProcessor.postProcess(_ as ChannelProcessingFilter) >> {ChannelProcessingFilter o -> o}
     }
+
+    def "invoke requiresChannel twice does not override"() {
+        setup:
+            loadConfig(DuplicateInvocationsDoesNotOverrideConfig)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            response.redirectedUrl == "https://localhost"
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class DuplicateInvocationsDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .requiresChannel()
+                    .anyRequest().requiresSecure()
+                    .and()
+                .requiresChannel()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.groovy
index 24f76b5d00..c713277220 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.groovy
@@ -29,6 +29,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
 import org.springframework.security.config.annotation.web.builders.HttpSecurity
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
+import org.springframework.security.config.annotation.web.configurers.JeeConfigurerTests.InvokeTwiceDoesNotOverride;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.access.ExceptionTranslationFilter
 import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint
@@ -178,4 +179,27 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
                 .formLogin()
         }
     }
+
+    def "invoke exceptionHandling twice does not override"() {
+        setup:
+            InvokeTwiceDoesNotOverrideConfig.AEP = Mock(AuthenticationEntryPoint)
+        when:
+            loadConfig(InvokeTwiceDoesNotOverrideConfig)
+        then:
+            findFilter(ExceptionTranslationFilter).authenticationEntryPoint == InvokeTwiceDoesNotOverrideConfig.AEP
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class InvokeTwiceDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+        static AuthenticationEntryPoint AEP
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .exceptionHandling()
+                    .authenticationEntryPoint(AEP)
+                    .and()
+                .exceptionHandling()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy
index 6f29ef1a49..f58411b355 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy
@@ -411,4 +411,36 @@ public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec {
                     .withUser("user").password("password").roles("USER")
         }
     }
+
+
+    def "invoke authorizeUrls twice does not reset"() {
+        setup:
+            loadConfig(InvokeTwiceDoesNotResetConfig)
+        when:
+            request.method = "POST"
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then: "Access is denied"
+            response.status == HttpServletResponse.SC_UNAUTHORIZED
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class InvokeTwiceDoesNotResetConfig extends WebSecurityConfigurerAdapter {
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .httpBasic()
+                    .and()
+                .authorizeUrls()
+                    .anyRequest().authenticated()
+                    .and()
+                .authorizeUrls()
+        }
+
+        @Override
+        protected void registerAuthentication(AuthenticationManagerBuilder auth)
+                throws Exception {
+            auth
+                .inMemoryAuthentication()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy
index 474b0345af..78bb4ba9cc 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy
@@ -196,6 +196,30 @@ class FormLoginConfigurerTests extends BaseSpringSpec {
         }
     }
 
+    def "duplicate formLogin does not override"() {
+        setup:
+            DuplicateInvocationsDoesNotOverrideConfig.FAILURE_HANDLER = Mock(AuthenticationFailureHandler)
+        when:
+            loadConfig(DuplicateInvocationsDoesNotOverrideConfig)
+        then:
+            findFilter(UsernamePasswordAuthenticationFilter).usernameParameter == "custom-username"
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class DuplicateInvocationsDoesNotOverrideConfig extends BaseWebConfig {
+        static AuthenticationFailureHandler FAILURE_HANDLER
+
+        @Override
+        protected void configure(HttpSecurity http) {
+            http
+                .formLogin()
+                    .usernameParameter("custom-username")
+                    .and()
+                .formLogin()
+        }
+    }
+
     def "formLogin ObjectPostProcessor"() {
         setup: "initialize the AUTH_FILTER as a mock"
             AnyObjectPostProcessor opp = Mock()
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.groovy
index 50daac2eeb..7d4365c9ff 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.groovy
@@ -99,4 +99,35 @@ class HttpBasicConfigurerTests extends BaseSpringSpec {
                 .inMemoryAuthentication()
         }
     }
-}
+
+    def "duplicate httpBasic invocations does not override"() {
+        setup:
+            DuplicateDoesNotOverrideConfig.ENTRY_POINT = Mock(AuthenticationEntryPoint)
+        when:
+            loadConfig(DuplicateDoesNotOverrideConfig)
+        then:
+            findFilter(ExceptionTranslationFilter).authenticationEntryPoint == DuplicateDoesNotOverrideConfig.ENTRY_POINT
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class DuplicateDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+        static AuthenticationEntryPoint ENTRY_POINT
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .httpBasic()
+                    .authenticationEntryPoint(ENTRY_POINT)
+                    .and()
+                .httpBasic()
+        }
+
+        @Override
+        protected void registerAuthentication(AuthenticationManagerBuilder auth)
+                throws Exception {
+            auth
+                .inMemoryAuthentication()
+        }
+    }
+}
\ No newline at end of file
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/JeeConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/JeeConfigurerTests.groovy
index aaa9bf5678..75dfe870c3 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/JeeConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/JeeConfigurerTests.groovy
@@ -15,10 +15,13 @@
  */
 package org.springframework.security.config.annotation.web.configurers
 
+import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
 import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter
 
@@ -43,4 +46,24 @@ class JeeConfigurerTests extends BaseSpringSpec {
         and: "J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource is registered with LifecycleManager"
             1 * opp.postProcess(_ as J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource) >> {J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource o -> o}
     }
+
+    def "invoke jee twice does not override"() {
+        when:
+            loadConfig(InvokeTwiceDoesNotOverride)
+        then:
+            findFilter(J2eePreAuthenticatedProcessingFilter).authenticationDetailsSource.j2eeMappableRoles == ["ROLE_USER"] as Set
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .jee()
+                    .mappableRoles("USER")
+                    .and()
+                .jee()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.groovy
index 7d7a2fe1b6..974e0e7fee 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/LogoutConfigurerTests.groovy
@@ -15,10 +15,13 @@
  */
 package org.springframework.security.config.annotation.web.configurers
 
+import org.springframework.context.annotation.Configuration
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
 import org.springframework.security.web.authentication.logout.LogoutFilter
 
 /**
@@ -40,4 +43,25 @@ class LogoutConfigurerTests extends BaseSpringSpec {
         then: "LogoutFilter is registered with LifecycleManager"
             1 * opp.postProcess(_ as LogoutFilter) >> {LogoutFilter o -> o}
     }
+
+    def "invoke logout twice does not override"() {
+        when:
+            loadConfig(InvokeTwiceDoesNotOverride)
+        then:
+            findFilter(LogoutFilter).filterProcessesUrl == "/custom/logout"
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .logout()
+                    .logoutUrl("/custom/logout")
+                    .and()
+                .logout()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/PortMapperConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/PortMapperConfigurerTests.groovy
new file mode 100644
index 0000000000..259aa555bb
--- /dev/null
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/PortMapperConfigurerTests.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2013 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.annotation.web.configurers
+
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.config.annotation.AnyObjectPostProcessor
+import org.springframework.security.config.annotation.BaseSpringSpec
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.configurers.SessionCreationPolicy;
+import org.springframework.security.web.access.ExceptionTranslationFilter
+import org.springframework.security.web.context.NullSecurityContextRepository;
+import org.springframework.security.web.context.SecurityContextPersistenceFilter
+import org.springframework.security.web.context.SecurityContextRepository
+import org.springframework.security.web.savedrequest.RequestCache
+import org.springframework.security.web.session.ConcurrentSessionFilter
+import org.springframework.security.web.session.SessionManagementFilter
+
+/**
+ *
+ * @author Rob Winch
+ */
+class PortMapperConfigurerTests extends BaseSpringSpec {
+
+    def "invoke portMapper twice does not override"() {
+        setup:
+            loadConfig(InvokeTwiceDoesNotOverride)
+            request.setServerPort(543)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            response.redirectedUrl == "https://localhost:123"
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .requiresChannel()
+                    .anyRequest().requiresSecure()
+                    .and()
+                .portMapper()
+                    .http(543).mapsTo(123)
+                    .and()
+                .portMapper()
+        }
+    }
+}
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy
index 63c5e9fb7e..b3d7f10707 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy
@@ -69,4 +69,19 @@ public class RememberMeConfigurerTests extends BaseSpringSpec {
         then: "RememberMeAuthenticationFilter is registered with LifecycleManager"
             1 * opp.postProcess(_ as RememberMeAuthenticationFilter) >> {RememberMeAuthenticationFilter o -> o}
     }
+
+    def "invoke rememberMe twice does not reset"() {
+        setup:
+            AnyObjectPostProcessor opp = Mock()
+            HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
+            UserDetailsService uds = authenticationBldr.getDefaultUserDetailsService()
+        when:
+            http
+                .rememberMe()
+                    .userDetailsService(authenticationBldr.getDefaultUserDetailsService())
+                    .and()
+                .rememberMe()
+        then: "RememberMeAuthenticationFilter is registered with LifecycleManager"
+            http.getConfigurer(RememberMeConfigurer).userDetailsService != null
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.groovy
index 8e722f0bba..71e13b4108 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.groovy
@@ -17,8 +17,9 @@ package org.springframework.security.config.annotation.web.configurers
 
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.web.savedrequest.RequestCache
 import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
 
 /**
@@ -40,4 +41,20 @@ class RequestCacheConfigurerTests extends BaseSpringSpec {
         then: "RequestCacheAwareFilter is registered with LifecycleManager"
             1 * opp.postProcess(_ as RequestCacheAwareFilter) >> {RequestCacheAwareFilter o -> o}
     }
+
+    def "invoke requestCache twice does not reset"() {
+        setup:
+            RequestCache RC = Mock()
+            AnyObjectPostProcessor opp = Mock()
+            HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
+        when:
+            http
+                .requestCache()
+                    .requestCache(RC)
+                    .and()
+                .requestCache()
+
+        then:
+            http.getSharedObject(RequestCache) == RC
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.groovy
index 51a9c6af82..5234f605a2 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.groovy
@@ -15,11 +15,15 @@
  */
 package org.springframework.security.config.annotation.web.configurers
 
+import org.springframework.context.annotation.Configuration
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
 import org.springframework.security.web.context.SecurityContextPersistenceFilter
+import org.springframework.security.web.context.SecurityContextRepository
 
 /**
  *
@@ -40,4 +44,28 @@ class SecurityContextConfigurerTests extends BaseSpringSpec {
         then: "SecurityContextPersistenceFilter is registered with LifecycleManager"
             1 * opp.postProcess(_ as SecurityContextPersistenceFilter) >> {SecurityContextPersistenceFilter o -> o}
     }
+
+    def "invoke securityContext twice does not override"() {
+        setup:
+            InvokeTwiceDoesNotOverrideConfig.SCR = Mock(SecurityContextRepository)
+        when:
+            loadConfig(InvokeTwiceDoesNotOverrideConfig)
+        then:
+            findFilter(SecurityContextPersistenceFilter).repo == InvokeTwiceDoesNotOverrideConfig.SCR
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+        static SecurityContextRepository SCR
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .securityContext()
+                    .securityContextRepository(SCR)
+                    .and()
+                .securityContext()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ServletApiConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ServletApiConfigurerTests.groovy
index 3a795a3507..b402e1f521 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ServletApiConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ServletApiConfigurerTests.groovy
@@ -107,4 +107,28 @@ class ServletApiConfigurerTests extends BaseSpringSpec {
                     .withUser("user").password("password").roles("USER")
         }
     }
+
+    def "invoke servletApi twice does not override"() {
+        setup:
+            InvokeTwiceDoesNotOverrideConfig.ENTRYPOINT = Mock(AuthenticationEntryPoint)
+        when:
+            loadConfig(InvokeTwiceDoesNotOverrideConfig)
+        then:
+            findFilter(SecurityContextHolderAwareRequestFilter).authenticationEntryPoint == InvokeTwiceDoesNotOverrideConfig.ENTRYPOINT
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+        static AuthenticationEntryPoint ENTRYPOINT
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .exceptionHandling()
+                    .authenticationEntryPoint(ENTRYPOINT)
+                    .and()
+                .exceptionHandling()
+        }
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.groovy
index a9ec6ac3bd..5abed94d4b 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.groovy
@@ -24,6 +24,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.web.configurers.SessionCreationPolicy;
 import org.springframework.security.web.access.ExceptionTranslationFilter
+import org.springframework.security.web.context.NullSecurityContextRepository;
 import org.springframework.security.web.context.SecurityContextPersistenceFilter
 import org.springframework.security.web.context.SecurityContextRepository
 import org.springframework.security.web.savedrequest.RequestCache
@@ -88,6 +89,27 @@ class SessionManagementConfigurerTests extends BaseSpringSpec {
 
     }
 
+    def "invoke sessionManagement twice does not override"() {
+        when:
+            loadConfig(InvokeTwiceDoesNotOverride)
+        then:
+            findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverride extends WebSecurityConfigurerAdapter {
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .sessionManagement()
+                    .sessionCreationPolicy(SessionCreationPolicy.stateless)
+                    .and()
+                .sessionManagement()
+        }
+
+    }
+
     def "sessionManagement ObjectPostProcessor"() {
         setup:
             AnyObjectPostProcessor opp = Mock()
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.groovy
index 4b8b9bc97a..f536a3f334 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.groovy
@@ -15,10 +15,13 @@
  */
 package org.springframework.security.config.annotation.web.configurers
 
+import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter
 
 /**
@@ -40,4 +43,18 @@ class X509ConfigurerTests extends BaseSpringSpec {
         then: "X509AuthenticationFilter is registered with LifecycleManager"
             1 * opp.postProcess(_ as X509AuthenticationFilter) >> {X509AuthenticationFilter o -> o}
     }
+
+    def "invoke x509 twice does not override"() {
+        setup:
+            AnyObjectPostProcessor opp = Mock()
+            HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
+        when:
+            http
+                .x509()
+                    .subjectPrincipalRegex(".*")
+                    .and()
+                .x509()
+        then:
+            http.getConfigurer(X509Configurer).subjectPrincipalRegex == ".*"
+    }
 }
diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurerTests.groovy
index f16ea46ca7..0507f412a9 100644
--- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurerTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurerTests.groovy
@@ -15,10 +15,13 @@
  */
 package org.springframework.security.config.annotation.web.configurers.openid
 
+import org.springframework.context.annotation.Configuration
 import org.springframework.security.config.annotation.AnyObjectPostProcessor
 import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
 import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper
 import org.springframework.security.core.userdetails.UserDetailsService
 import org.springframework.security.openid.OpenIDAuthenticationFilter
@@ -48,4 +51,38 @@ class OpenIDLoginConfigurerTests extends BaseSpringSpec {
         and: "OpenIDAuthenticationProvider is registered with LifecycleManager"
             1 * opp.postProcess(_ as OpenIDAuthenticationProvider) >> {OpenIDAuthenticationProvider o -> o}
     }
+
+    def "invoke openidLogin twice does not override"() {
+        setup:
+            loadConfig(InvokeTwiceDoesNotOverrideConfig)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            response.redirectedUrl.endsWith("/login/custom")
+
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class InvokeTwiceDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void registerAuthentication(AuthenticationManagerBuilder auth)
+                throws Exception {
+            auth
+                .inMemoryAuthentication()
+        }
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http
+                .authorizeUrls()
+                    .anyRequest().authenticated()
+                    .and()
+                .openidLogin()
+                    .loginPage("/login/custom")
+                    .and()
+                .openidLogin()
+        }
+    }
 }