diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java index 3c0a736c0c..cd191c6c21 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java @@ -22,6 +22,7 @@ import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.PortMapper; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; @@ -30,10 +31,8 @@ import org.springframework.security.web.authentication.LoginUrlAuthenticationEnt import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; -import org.springframework.security.web.util.AntPathRequestMatcher; import org.springframework.security.web.util.MediaTypeRequestMatcher; import org.springframework.security.web.util.RequestMatcher; import org.springframework.web.accept.ContentNegotiationStrategy; @@ -78,10 +77,10 @@ public abstract class AbstractAuthenticationFilterConfigurer */ protected T loginPage(String loginPage) { - this.loginPage = loginPage; - this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage); + setLoginPage(loginPage); + updateAuthenticationDefaults(); this.customLoginPage = true; return getSelf(); } /** - * - * @return true if a custom login page has been specified, else false - */ - public final boolean isCustomLoginPage() { - return customLoginPage; - } + * + * @return true if a custom login page has been specified, else false + */ + public final boolean isCustomLoginPage() { + return customLoginPage; + } - /** - * Gets the Authentication Filter - * @return - */ - protected final F getAuthenticationFilter() { - return authFilter; - } + /** + * Gets the Authentication Filter + * + * @return + */ + protected final F getAuthenticationFilter() { + return authFilter; + } - /** - * Gets the login page - * @return the login page - */ - protected final String getLoginPage() { - return loginPage; - } + /** + * Gets the login page + * + * @return the login page + */ + protected final String getLoginPage() { + return loginPage; + } - /** - * Gets the URL to submit an authentication request to (i.e. where - * username/password must be submitted) - * - * @return the URL to submit an authentication request to - */ - protected final String getLoginProcessingUrl() { - return loginProcessingUrl; - } + /** + * Gets the URL to submit an authentication request to (i.e. where + * username/password must be submitted) + * + * @return the URL to submit an authentication request to + */ + protected final String getLoginProcessingUrl() { + return loginProcessingUrl; + } - /** - * Gets the URL to send users to if authentication fails - * @return - */ - protected final String getFailureUrl() { - return failureUrl; - } + /** + * Gets the URL to send users to if authentication fails + * + * @return + */ + protected final String getFailureUrl() { + return failureUrl; + } + /** + * Updates the default values for authentication. + * + * @throws Exception + */ + private void updateAuthenticationDefaults() { + if (loginProcessingUrl == null) { + loginProcessingUrl(loginPage); + } + if (failureHandler == null) { + failureUrl(loginPage + "?error"); + } + + final LogoutConfigurer logoutConfigurer = getBuilder() + .getConfigurer(LogoutConfigurer.class); + if (logoutConfigurer != null + && !logoutConfigurer.isCustomLogoutSuccess()) { + logoutConfigurer.logoutSuccessUrl(loginPage + "?logout"); + } + } + + /** + * Sets the loginPage and updates the {@link AuthenticationEntryPoint}. + * @param loginPage + */ + private void setLoginPage(String loginPage) { + this.loginPage = loginPage; + this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint( + loginPage); + } @SuppressWarnings("unchecked") private T getSelf() { diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java index 08fd69e7e2..23c52b1aca 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java @@ -70,7 +70,7 @@ public final class FormLoginConfigurer> extends * @see HttpSecurity#formLogin() */ public FormLoginConfigurer() { - super(new UsernamePasswordAuthenticationFilter(),"/login"); + super(new UsernamePasswordAuthenticationFilter(),null); usernameParameter("username"); passwordParameter("password"); } @@ -147,6 +147,31 @@ public final class FormLoginConfigurer> extends * </form> * * + *

Impact on other defaults

+ * + * Updating this value, also impacts a number of other default values. For example, + * the following are the default values when only formLogin() was specified. + * + *
    + *
  • /login GET - the login form
  • + *
  • /login POST - process the credentials and if valid authenticate the + * user
  • + *
  • /login?error GET - redirect here for failed authentication attempts
  • + *
  • /login?logout GET - redirect here after successfully logging out
  • + *
+ * + * If "/authenticate" was passed to this method it update the defaults as shown + * below: + * + *
    + *
  • /authenticate GET - the login form
  • + *
  • /authenticate POST - process the credentials and if valid authenticate the + * user
  • + *
  • /authenticate?error GET - redirect here for failed authentication attempts
  • + *
  • /authenticate?logout GET - redirect here after successfully logging out
  • + *
+ * + * * @param loginPage * the login page to redirect to if authentication is required * (i.e. "/login") diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java index 8c7616d0eb..ef5f918149 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java @@ -223,7 +223,7 @@ public final class LogoutConfigurer> extends Ab * * @return true if logout success handling has been customized, else false */ - private boolean isCustomLogoutSuccess() { + boolean isCustomLogoutSuccess() { return customLogoutSuccess; } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java index 7c045a4c1d..a202608d25 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java @@ -225,6 +225,31 @@ public final class OpenIDLoginConfigurer> exten * {@link OpenIDAuthenticationFilter#DEFAULT_CLAIMED_IDENTITY_FIELD} * * + * + *

Impact on other defaults

+ * + * Updating this value, also impacts a number of other default values. For example, + * the following are the default values when only formLogin() was specified. + * + *
    + *
  • /login GET - the login form
  • + *
  • /login POST - process the credentials and if valid authenticate the + * user
  • + *
  • /login?error GET - redirect here for failed authentication attempts
  • + *
  • /login?logout GET - redirect here after successfully logging out
  • + *
+ * + * If "/authenticate" was passed to this method it update the defaults as shown + * below: + * + *
    + *
  • /authenticate GET - the login form
  • + *
  • /authenticate POST - process the credentials and if valid authenticate the + * user
  • + *
  • /authenticate?error GET - redirect here for failed authentication attempts
  • + *
  • /authenticate?logout GET - redirect here after successfully logging out
  • + *
+ * * @param loginPage the login page to redirect to if authentication is required (i.e. "/login") * @return the {@link FormLoginConfigurer} for additional customization */ diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy index 8a2f65af32..ba6f1609ce 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy @@ -173,7 +173,7 @@ public class SampleWebSecurityConfigurerAdapterTests extends BaseSpringSpec { .anyRequest().hasRole("USER") .and() .formLogin() - .loginUrl("/login") + .loginPage("/login") // set permitAll for all URLs associated with Form Login .permitAll(); } @@ -314,7 +314,7 @@ public class SampleWebSecurityConfigurerAdapterTests extends BaseSpringSpec { .anyRequest().hasRole("USER") .and() .formLogin() - .loginUrl("/login") + .loginPage("/login") .permitAll(); } } 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 cbf4f23a98..44776aaa85 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 @@ -41,7 +41,7 @@ import org.springframework.security.web.authentication.logout.LogoutFilter import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy import org.springframework.security.web.context.SecurityContextPersistenceFilter import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter -import org.springframework.security.web.csrf.CsrfFilter; +import org.springframework.security.web.csrf.CsrfFilter import org.springframework.security.web.header.HeaderWriterFilter import org.springframework.security.web.savedrequest.RequestCacheAwareFilter import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter @@ -49,6 +49,8 @@ import org.springframework.security.web.session.SessionManagementFilter import org.springframework.security.web.util.AnyRequestMatcher import org.springframework.test.util.ReflectionTestUtils +import spock.lang.Unroll + /** * * @author Rob Winch @@ -106,7 +108,7 @@ class FormLoginConfigurerTests extends BaseSpringSpec { .anyRequest().hasRole("USER") .and() .formLogin() - .loginUrl("/login") + .loginPage("/login") } } @@ -144,6 +146,46 @@ class FormLoginConfigurerTests extends BaseSpringSpec { } } + @Unroll + def "FormLogin loginConventions changes defaults"() { + when: "load formLogin() with permitAll" + loadConfig(FormLoginDefaultsConfig) + MockHttpServletResponse response = new MockHttpServletResponse() + request = new MockHttpServletRequest(servletPath : servletPath, requestURI: servletPath, queryString: query, method: method) + setupCsrf() + + then: "the other default login/logout URLs are updated and granted access" + springSecurityFilterChain.doFilter(request, response, new MockFilterChain()) + response.redirectedUrl == redirectUrl + + where: + servletPath | method | query | redirectUrl + "/authenticate" | "GET" | null | null + "/authenticate" | "POST" | null | "/authenticate?error" + "/authenticate" | "GET" | "error" | null + "/logout" | "POST" | null | "/authenticate?logout" + "/authenticate" | "GET" | "logout"| null + } + + @Configuration + @EnableWebSecurity + static class FormLoginDefaultsConfig extends BaseWebConfig { + + @Override + protected void configure(HttpSecurity http) { + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .loginPage("/authenticate") + .permitAll() + .and() + .logout() + .permitAll() + } + } + def "FormLogin uses PortMapper"() { when: "load formLogin() with permitAll" FormLoginUsesPortMapperConfig.PORT_MAPPER = Mock(PortMapper) diff --git a/samples/openid-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/openid-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java index d6ad5cf1d5..0eb1771d0b 100644 --- a/samples/openid-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ b/samples/openid-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java @@ -18,7 +18,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .anyRequest().authenticated() .and() .openidLogin() - .loginPage("/login") .permitAll() .authenticationUserDetailsService(new CustomUserDetailsService()) .attributeExchange("https://www.google.com/.*")