mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 14:22:47 +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<>());
|
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
|
* Allows configuring a {@link PortMapper} that is available from
|
||||||
* {@link HttpSecurity#getSharedObject(Class)}. Other provided
|
* {@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.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.SecurityMockMvcRequestPostProcessors.csrf;
|
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.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
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
|
@Test
|
||||||
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnSessionManagementFilter() {
|
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnSessionManagementFilter() {
|
||||||
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user