Spring Boot Session Expiry with Undertow
I’ve been working on a Spring Boot project recently and I needed to set the session expiry time.
I was using spring-boot-starter-web, and I was getting inconsistent behaviour from Tomcat webserver (I suspect that Tomcat was setting it’s own timeout value, which was overriding the 10 second in application.properties).
I decided to use Undertow.
I did this by putting an exclusion in the pom.xml, which excludes the Tomcat dependency, and then adding the Undertow dependency.
I also added an explicit SessionTimeoutListener with an overridden sessionDestroyed method. This is referenced from ListenerConfig.java. This has an explicit Session has timed out debug message.
application.properties - 10 second timeout:
server.servlet.session.timeout=10
logging.level.root=INFO
pom.xml - with tomcat exclusion and undertow dependency:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Session</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Archetype - Session</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
SessionTimeoutListener.java - with sessionDestroyed method:
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
public class SessionTimeoutListener implements HttpSessionListener {
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("Session has timed out: " + se.getSession().getId());
}
}
ListenerConfig.java - with sessionTimeoutListener method:
import com.chocksaway.session.listener.SessionTimeoutListener;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ListenerConfig {
@Bean
public ServletListenerRegistrationBean<SessionTimeoutListener> sessionTimeoutListener() {
return new ServletListenerRegistrationBean<>(new SessionTimeoutListener());
}
}
SecurityConfig.java - with sessionManagement:
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.requestMatchers("/auth/login").permitAll()
.requestMatchers("/auth/logout").permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
.loginPage("/auth/login")
.defaultSuccessUrl("/home", true)
.successHandler(authenticationSuccessHandler())
)
.logout(logout -> logout.logoutUrl("/auth/logout")
.logoutSuccessUrl("/auth/login")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
)
.sessionManagement(sessionManagement -> sessionManagement
.invalidSessionUrl("/auth/login")
.maximumSessions(1)
.expiredUrl("/auth/login")
);
return http.build();
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return (HttpServletRequest request, HttpServletResponse response,
org.springframework.security.core.Authentication authentication) -> {
request.getSession().setAttribute("username", authentication.getName());
response.sendRedirect("/home");
};
}
}