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 75e818caf9..4c0fae24e0 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
@@ -962,8 +962,7 @@ public final class HttpSecurity extends
* .authorizeRequests()
* .antMatchers("/**").hasRole("USER")
* .and()
- * .formLogin()
- * .and()
+ * .formLogin(withDefaults())
* // sample logout customization
* .logout(logout ->
* logout.deleteCookies("remove")
@@ -1112,6 +1111,71 @@ public final class HttpSecurity extends
return getOrApply(new FormLoginConfigurer<>());
}
+ /**
+ * Specifies to support form based authentication. If
+ * {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page
+ * will be generated.
+ *
+ *
Example Configurations
+ *
+ * The most basic configuration defaults to automatically generating a login page at
+ * the URL "/login", redirecting to "/login?error" for authentication failure. The
+ * details of the login page can be found on
+ * {@link FormLoginConfigurer#loginPage(String)}
+ *
+ *
+ * @Configuration
+ * @EnableWebSecurity
+ * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * @Override
+ * protected void configure(HttpSecurity http) throws Exception {
+ * http
+ * .authorizeRequests()
+ * .antMatchers("/**").hasRole("USER")
+ * .and()
+ * .formLogin(withDefaults());
+ * }
+ * }
+ *
+ *
+ * The configuration below demonstrates customizing the defaults.
+ *
+ *
+ * @Configuration
+ * @EnableWebSecurity
+ * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * @Override
+ * protected void configure(HttpSecurity http) throws Exception {
+ * http
+ * .authorizeRequests()
+ * .antMatchers("/**").hasRole("USER")
+ * .and()
+ * .formLogin(formLogin ->
+ * formLogin
+ * .usernameParameter("username")
+ * .passwordParameter("password")
+ * .loginPage("/authentication/login")
+ * .failureUrl("/authentication/login?failed")
+ * .loginProcessingUrl("/authentication/login/process")
+ * );
+ * }
+ * }
+ *
+ *
+ * @see FormLoginConfigurer#loginPage(String)
+ *
+ * @param formLoginCustomizer the {@link Customizer} to provide more options for
+ * the {@link FormLoginConfigurer}
+ * @return the {@link HttpSecurity} for further customizations
+ * @throws Exception
+ */
+ public HttpSecurity formLogin(Customizer> formLoginCustomizer) throws Exception {
+ formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
+ return HttpSecurity.this;
+ }
+
/**
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.
*
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java
index be2aad0815..22e88597ce 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java
@@ -55,6 +55,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@@ -528,8 +529,7 @@ public class CsrfConfigurerTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
- .formLogin()
- .and()
+ .formLogin(withDefaults())
.csrf(csrf -> csrf.csrfTokenRepository(REPO));
// @formatter:on
}
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java
index 4211806094..ae5a2379e4 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java
@@ -42,6 +42,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@@ -195,6 +196,81 @@ public class FormLoginConfigurerTests {
}
}
+ @Test
+ public void loginWhenFormLoginDefaultsInLambdaThenHasDefaultUsernameAndPasswordParameterNames() throws Exception {
+ this.spring.register(FormLoginInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(formLogin().user("username", "user").password("password", "password"))
+ .andExpect(status().isFound())
+ .andExpect(redirectedUrl("/"));
+ }
+
+ @Test
+ public void loginWhenFormLoginDefaultsInLambdaThenHasDefaultFailureUrl() throws Exception {
+ this.spring.register(FormLoginInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(formLogin().user("invalid"))
+ .andExpect(status().isFound())
+ .andExpect(redirectedUrl("/login?error"));
+ }
+
+ @Test
+ public void loginWhenFormLoginDefaultsInLambdaThenHasDefaultSuccessUrl() throws Exception {
+ this.spring.register(FormLoginInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(formLogin())
+ .andExpect(status().isFound())
+ .andExpect(redirectedUrl("/"));
+ }
+
+ @Test
+ public void getLoginPageWhenFormLoginDefaultsInLambdaThenNotSecured() throws Exception {
+ this.spring.register(FormLoginInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(get("/login"))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void loginWhenFormLoginDefaultsInLambdaThenSecured() throws Exception {
+ this.spring.register(FormLoginInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(post("/login"))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void requestProtectedWhenFormLoginDefaultsInLambdaThenRedirectsToLogin() throws Exception {
+ this.spring.register(FormLoginInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(get("/private"))
+ .andExpect(status().isFound())
+ .andExpect(redirectedUrl("http://localhost/login"));
+ }
+
+ @EnableWebSecurity
+ static class FormLoginInLambdaConfig extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .anyRequest().hasRole("USER")
+ .and()
+ .formLogin(withDefaults());
+ // @formatter:on
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ // @formatter:off
+ auth
+ .inMemoryAuthentication()
+ .withUser(PasswordEncodedUser.user());
+ // @formatter:on
+ }
+ }
+
@Test
public void getLoginPageWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception {
this.spring.register(FormLoginConfigPermitAll.class).autowire();
@@ -297,6 +373,33 @@ public class FormLoginConfigurerTests {
}
}
+ @Test
+ public void getLoginPageWhenCustomLoginPageInLambdaThenPermittedAndNoRedirect() throws Exception {
+ this.spring.register(FormLoginDefaultsInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(get("/authenticate"))
+ .andExpect(redirectedUrl(null));
+ }
+
+ @EnableWebSecurity
+ static class FormLoginDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .anyRequest().hasRole("USER")
+ .and()
+ .formLogin(formLogin ->
+ formLogin
+ .loginPage("/authenticate")
+ .permitAll()
+ )
+ .logout(LogoutConfigurer::permitAll);
+ // @formatter:on
+ }
+ }
+
@Test
public void loginWhenCustomLoginProcessingUrlThenRedirectsToHome() throws Exception {
this.spring.register(FormLoginLoginProcessingUrlConfig.class).autowire();
@@ -340,6 +443,50 @@ public class FormLoginConfigurerTests {
}
}
+ @Test
+ public void loginWhenCustomLoginProcessingUrlInLambdaThenRedirectsToHome() throws Exception {
+ this.spring.register(FormLoginLoginProcessingUrlInLambdaConfig.class).autowire();
+
+ this.mockMvc.perform(formLogin("/loginCheck"))
+ .andExpect(status().isFound())
+ .andExpect(redirectedUrl("/"));
+ }
+
+ @EnableWebSecurity
+ static class FormLoginLoginProcessingUrlInLambdaConfig extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin(formLogin ->
+ formLogin
+ .loginProcessingUrl("/loginCheck")
+ .loginPage("/login")
+ .defaultSuccessUrl("/", true)
+ .permitAll()
+ )
+ .logout(logout ->
+ logout
+ .logoutSuccessUrl("/login")
+ .logoutUrl("/logout")
+ .deleteCookies("JSESSIONID")
+ );
+ // @formatter:on
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ // @formatter:off
+ auth
+ .inMemoryAuthentication()
+ .withUser(PasswordEncodedUser.user());
+ // @formatter:on
+ }
+ }
+
@Test
public void requestWhenCustomPortMapperThenPortMapperUsed() throws Exception {
FormLoginUsesPortMapperConfig.PORT_MAPPER = mock(PortMapper.class);