feat: track user login and logout timestamps in session management
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 4m15s
AS Talange CI/CD Pipeline / Deploy to Test Environment (push) Successful in 5m55s

This commit is contained in:
2026-07-31 10:26:14 +02:00
parent df217bc923
commit 1737087062
5 changed files with 221 additions and 20 deletions
@@ -36,4 +36,10 @@ public class AppUser {
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
@Column(name = "last_login_at")
private java.time.LocalDateTime lastLoginAt;
@Column(name = "last_logout_at")
private java.time.LocalDateTime lastLogoutAt;
}
@@ -0,0 +1,79 @@
package com.astalange.core.security;
import com.astalange.core.entity.AppUser;
import com.astalange.core.repository.AppUserRepository;
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.session.HttpSessionDestroyedEvent;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Component
public class AuthenticationEventListener {
private final AppUserRepository userRepository;
public AuthenticationEventListener(AppUserRepository userRepository) {
this.userRepository = userRepository;
}
@EventListener
@Transactional
public void onAuthenticationSuccess(AuthenticationSuccessEvent event) {
String username = extractUsername(event.getAuthentication().getPrincipal());
if (username != null) {
userRepository.findByUsername(username).ifPresent(user -> {
user.setLastLoginAt(LocalDateTime.now());
userRepository.save(user);
});
}
}
@EventListener
@Transactional
public void onLogoutSuccess(LogoutSuccessEvent event) {
if (event.getAuthentication() != null) {
String username = extractUsername(event.getAuthentication().getPrincipal());
if (username != null) {
userRepository.findByUsername(username).ifPresent(user -> {
user.setLastLogoutAt(LocalDateTime.now());
userRepository.save(user);
});
}
}
}
@EventListener
@Transactional
public void onSessionDestroyed(HttpSessionDestroyedEvent event) {
List<SecurityContext> contexts = event.getSecurityContexts();
for (SecurityContext context : contexts) {
if (context != null && context.getAuthentication() != null) {
String username = extractUsername(context.getAuthentication().getPrincipal());
if (username != null) {
userRepository.findByUsername(username).ifPresent(user -> {
if (user.getLastLogoutAt() == null || (user.getLastLoginAt() != null && user.getLastLogoutAt().isBefore(user.getLastLoginAt()))) {
user.setLastLogoutAt(LocalDateTime.now());
userRepository.save(user);
}
});
}
}
}
}
private String extractUsername(Object principal) {
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else if (principal instanceof String) {
return (String) principal;
}
return null;
}
}
@@ -0,0 +1,2 @@
ALTER TABLE app_user ADD COLUMN last_login_at TIMESTAMP;
ALTER TABLE app_user ADD COLUMN last_logout_at TIMESTAMP;