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
@@ -1,5 +1,8 @@
package com.astalange.web.controller;
import com.astalange.core.entity.AppUser;
import com.astalange.core.entity.Role;
import com.astalange.core.repository.AppUserRepository;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetails;
@@ -8,7 +11,9 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Controller
@@ -16,22 +21,73 @@ import java.util.stream.Collectors;
public class SessionController {
private final SessionRegistry sessionRegistry;
private final AppUserRepository userRepository;
public SessionController(SessionRegistry sessionRegistry) {
public SessionController(SessionRegistry sessionRegistry, AppUserRepository userRepository) {
this.sessionRegistry = sessionRegistry;
this.userRepository = userRepository;
}
public static class UserSessionDTO {
private final Long id;
private final String username;
private final boolean online;
private final LocalDateTime lastLoginAt;
private final LocalDateTime lastLogoutAt;
private final String roles;
public UserSessionDTO(Long id, String username, boolean online, LocalDateTime lastLoginAt, LocalDateTime lastLogoutAt, String roles) {
this.id = id;
this.username = username;
this.online = online;
this.lastLoginAt = lastLoginAt;
this.lastLogoutAt = lastLogoutAt;
this.roles = roles;
}
public Long getId() { return id; }
public String getUsername() { return username; }
public boolean isOnline() { return online; }
public LocalDateTime getLastLoginAt() { return lastLoginAt; }
public LocalDateTime getLastLogoutAt() { return lastLogoutAt; }
public String getRoles() { return roles; }
}
@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public String viewSessions(Model model) {
List<Object> principals = sessionRegistry.getAllPrincipals();
List<String> activeUsers = principals.stream()
Set<String> activeUsernames = principals.stream()
.filter(principal -> principal instanceof UserDetails)
.map(principal -> ((UserDetails) principal).getUsername())
.collect(Collectors.toList());
model.addAttribute("activeUsers", activeUsers);
model.addAttribute("activeCount", activeUsers.size());
.collect(Collectors.toSet());
List<AppUser> allUsers = userRepository.findAll();
List<UserSessionDTO> userSessions = allUsers.stream().map(user -> {
boolean isOnline = activeUsernames.contains(user.getUsername());
String rolesStr = user.getRoles().stream()
.map(Role::getName)
.map(r -> r.replace("ROLE_", ""))
.collect(Collectors.joining(", "));
return new UserSessionDTO(
user.getId(),
user.getUsername(),
isOnline,
user.getLastLoginAt(),
user.getLastLogoutAt(),
rolesStr
);
}).collect(Collectors.toList());
long activeCount = userSessions.stream().filter(UserSessionDTO::isOnline).count();
long offlineCount = userSessions.size() - activeCount;
model.addAttribute("userSessions", userSessions);
model.addAttribute("activeCount", activeCount);
model.addAttribute("offlineCount", offlineCount);
model.addAttribute("totalCount", userSessions.size());
return "admin/sessions";
}
}