mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 06:12:27 +00:00
Allow configuration of session management through nested builder
Issue: gh-5557
This commit is contained in:
parent
6fd515813c
commit
6fbea88e1e
@ -530,6 +530,66 @@ public final class HttpSecurity extends
|
||||
return getOrApply(new SessionManagementConfigurer<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring of Session Management.
|
||||
*
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* The following configuration demonstrates how to enforce that only a single instance
|
||||
* of a user is authenticated at a time. If a user authenticates with the username
|
||||
* "user" without logging out and an attempt to authenticate with "user" is made the
|
||||
* first session will be forcibly terminated and sent to the "/login?expired" URL.
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class SessionManagementSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests()
|
||||
* .anyRequest().hasRole("USER")
|
||||
* .and()
|
||||
* .formLogin(formLogin ->
|
||||
* formLogin
|
||||
* .permitAll()
|
||||
* )
|
||||
* .sessionManagement(sessionManagement ->
|
||||
* sessionManagement
|
||||
* .maximumSessions(1)
|
||||
* .expiredUrl("/login?expired")
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* When using {@link SessionManagementConfigurer#maximumSessions(int)}, do not forget
|
||||
* to configure {@link HttpSessionEventPublisher} for the application to ensure that
|
||||
* expired sessions are cleaned up.
|
||||
*
|
||||
* In a web.xml this can be configured using the following:
|
||||
*
|
||||
* <pre>
|
||||
* <listener>
|
||||
* <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
|
||||
* </listener>
|
||||
* </pre>
|
||||
*
|
||||
* Alternatively,
|
||||
* {@link AbstractSecurityWebApplicationInitializer#enableHttpSessionEventPublisher()}
|
||||
* could return true.
|
||||
*
|
||||
* @param sessionManagementCustomizer the {@link Customizer} to provide more options for
|
||||
* the {@link SessionManagementConfigurer}
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public HttpSecurity sessionManagement(Customizer<SessionManagementConfigurer<HttpSecurity>> sessionManagementCustomizer) throws Exception {
|
||||
sessionManagementCustomizer.customize(getOrApply(new SessionManagementConfigurer<>()));
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring a {@link PortMapper} that is available from
|
||||
* {@link HttpSecurity#getSharedObject(Class)}. Other provided
|
||||
|
@ -54,6 +54,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.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
@ -262,6 +263,73 @@ public class SessionManagementConfigurerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUserLoggedInAndMaxSessionsOneInLambdaThenLoginPrevented() throws Exception {
|
||||
this.spring.register(ConcurrencyControlInLambdaConfig.class).autowire();
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.with(csrf())
|
||||
.param("username", "user")
|
||||
.param("password", "password"));
|
||||
|
||||
this.mvc.perform(post("/login")
|
||||
.with(csrf())
|
||||
.param("username", "user")
|
||||
.param("password", "password"))
|
||||
.andExpect(status().isFound())
|
||||
.andExpect(redirectedUrl("/login?error"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class ConcurrencyControlInLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.formLogin(withDefaults())
|
||||
.sessionManagement(sessionManagement ->
|
||||
sessionManagement
|
||||
.maximumSessions(1)
|
||||
.maxSessionsPreventsLogin(true)
|
||||
);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
// @formatter:off
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user());
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenSessionCreationPolicyStateLessInLambdaThenNoSessionCreated() throws Exception {
|
||||
this.spring.register(SessionCreationPolicyStateLessInLambdaConfig.class).autowire();
|
||||
|
||||
MvcResult mvcResult = this.mvc.perform(get("/"))
|
||||
.andReturn();
|
||||
HttpSession session = mvcResult.getRequest().getSession(false);
|
||||
|
||||
assertThat(session).isNull();
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class SessionCreationPolicyStateLessInLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.sessionManagement(sessionManagement ->
|
||||
sessionManagement
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
);
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnSessionManagementFilter() {
|
||||
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||
|
Loading…
x
Reference in New Issue
Block a user