Allow configuration of form login through nested builder
Issue: gh-5557
This commit is contained in:
parent
758397f102
commit
a9a1f8ee53
|
@ -962,8 +962,7 @@ public final class HttpSecurity extends
|
||||||
* .authorizeRequests()
|
* .authorizeRequests()
|
||||||
* .antMatchers("/**").hasRole("USER")
|
* .antMatchers("/**").hasRole("USER")
|
||||||
* .and()
|
* .and()
|
||||||
* .formLogin()
|
* .formLogin(withDefaults())
|
||||||
* .and()
|
|
||||||
* // sample logout customization
|
* // sample logout customization
|
||||||
* .logout(logout ->
|
* .logout(logout ->
|
||||||
* logout.deleteCookies("remove")
|
* logout.deleteCookies("remove")
|
||||||
|
@ -1112,6 +1111,71 @@ public final class HttpSecurity extends
|
||||||
return getOrApply(new FormLoginConfigurer<>());
|
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.
|
||||||
|
*
|
||||||
|
* <h2>Example Configurations</h2>
|
||||||
|
*
|
||||||
|
* 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)}
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeRequests()
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* .and()
|
||||||
|
* .formLogin(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below demonstrates customizing the defaults.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @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")
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @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<FormLoginConfigurer<HttpSecurity>> 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.
|
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.
|
||||||
* <br>
|
* <br>
|
||||||
|
|
|
@ -55,6 +55,7 @@ import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
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.csrf;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
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 {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
http
|
http
|
||||||
.formLogin()
|
.formLogin(withDefaults())
|
||||||
.and()
|
|
||||||
.csrf(csrf -> csrf.csrfTokenRepository(REPO));
|
.csrf(csrf -> csrf.csrfTokenRepository(REPO));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
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.formLogin;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
|
||||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
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
|
@Test
|
||||||
public void getLoginPageWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception {
|
public void getLoginPageWhenFormLoginPermitAllThenPermittedAndNoRedirect() throws Exception {
|
||||||
this.spring.register(FormLoginConfigPermitAll.class).autowire();
|
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
|
@Test
|
||||||
public void loginWhenCustomLoginProcessingUrlThenRedirectsToHome() throws Exception {
|
public void loginWhenCustomLoginProcessingUrlThenRedirectsToHome() throws Exception {
|
||||||
this.spring.register(FormLoginLoginProcessingUrlConfig.class).autowire();
|
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
|
@Test
|
||||||
public void requestWhenCustomPortMapperThenPortMapperUsed() throws Exception {
|
public void requestWhenCustomPortMapperThenPortMapperUsed() throws Exception {
|
||||||
FormLoginUsesPortMapperConfig.PORT_MAPPER = mock(PortMapper.class);
|
FormLoginUsesPortMapperConfig.PORT_MAPPER = mock(PortMapper.class);
|
||||||
|
|
Loading…
Reference in New Issue