| tags | ||||
|---|---|---|---|---|
|
This document contains code examples and implementation details for Spring Session. For theory, patterns, and best practices, see Security - Session-Based Authentication.
<!-- Spring Session with Redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
<!-- Or with JDBC -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>// Spring Session with Redis
implementation 'org.springframework.session:spring-session-data-redis'
implementation 'io.lettuce:lettuce-core'
// Or with JDBC
implementation 'org.springframework.session:spring-session-jdbc'@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) // 30 minutes
public class SessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}# Redis Session Store
spring.session.store-type=redis
spring.session.redis.flush-mode=on_save
spring.session.redis.namespace=spring:session
spring.session.timeout=1800s
# Redis Connection
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=spring:
session:
store-type: redis
timeout: 1800s
redis:
namespace: spring:session
flush-mode: on_save
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}@Configuration
@EnableJdbcHttpSession
public class SessionConfig {
// Spring Session will auto-configure JDBC session store
}# JDBC Session Store
spring.session.store-type=jdbc
spring.session.jdbc.initialize-schema=always
spring.session.timeout=1800s
# Database
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=user
spring.datasource.password=pass@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry())
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard", true)
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
);
return http.build();
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.maximumSessions(1) // Only one session per user
.maxSessionsPreventsLogin(true) // Block new login if max reached
.expiredUrl("/login?expired")
);
return http.build();
}
}http.sessionManagement(session -> session
.sessionFixation()
.changeSessionId() // Default: changes session ID after login
// .migrateSession() // Alternative: creates new session
// .newSession() // Alternative: creates new session and migrates data
);@RestController
public class UserController {
@GetMapping("/profile")
public UserProfile getProfile(HttpSession session) {
String username = (String) session.getAttribute("username");
// Access session attributes
return userService.getProfile(username);
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request,
HttpSession session) {
User user = userService.authenticate(request);
session.setAttribute("username", user.getUsername());
session.setAttribute("userId", user.getId());
return ResponseEntity.ok().build();
}
}@Service
public class SessionService {
@Autowired
private SessionRepository sessionRepository;
public void addAttributeToSession(String sessionId, String key, Object value) {
Session session = sessionRepository.findById(sessionId);
if (session != null) {
session.setAttribute(key, value);
sessionRepository.save(session);
}
}
public void invalidateSession(String sessionId) {
sessionRepository.deleteById(sessionId);
}
public List<Session> getAllActiveSessions() {
return sessionRepository.findAll().stream()
.filter(s -> s.getLastAccessedTime()
.isAfter(Instant.now().minus(Duration.ofMinutes(30))))
.collect(Collectors.toList());
}
}@Configuration
@EnableRedisHttpSession(
maxInactiveIntervalInSeconds = 1800, // 30 minutes
redisNamespace = "spring:session"
)
public class SessionConfig {
// Configuration
}@Configuration
public class CookieConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
serializer.setUseHttpOnlyCookie(true);
serializer.setUseSecureCookie(true); // HTTPS only
serializer.setSameSite("Strict"); // CSRF protection
return serializer;
}
}@Component
public class SessionEventListener {
@EventListener
public void onSessionCreated(SessionCreatedEvent event) {
Session session = event.getSession();
log.info("Session created: {}", session.getId());
}
@EventListener
public void onSessionDestroyed(SessionDestroyedEvent event) {
log.info("Session destroyed: {}", event.getSessionId());
}
@EventListener
public void onSessionExpired(SessionExpiredEvent event) {
log.info("Session expired: {}", event.getSessionId());
}
}http.rememberMe(remember -> remember
.key("uniqueAndSecret")
.tokenValiditySeconds(86400) // 24 hours
.userDetailsService(userDetailsService)
);@RestController
@RequestMapping("/api/session")
public class SessionController {
@Autowired
private SessionRepository sessionRepository;
@GetMapping("/current")
public Map<String, Object> getCurrentSession(HttpSession session) {
Map<String, Object> sessionData = new HashMap<>();
sessionData.put("id", session.getId());
sessionData.put("creationTime", session.getCreationTime());
sessionData.put("lastAccessedTime", session.getLastAccessedTime());
sessionData.put("maxInactiveInterval", session.getMaxInactiveInterval());
return sessionData;
}
@PostMapping("/invalidate")
public ResponseEntity<?> invalidateSession(HttpSession session) {
session.invalidate();
return ResponseEntity.ok().build();
}
}Solution: Ensure you're using an external session store (Redis, JDBC) instead of in-memory storage.
Solution: Check maxInactiveIntervalInSeconds configuration and ensure session repository is properly configured.
Solution: Ensure SessionRegistry bean is properly configured and session events are being published.