You can use the standard HttpSessionListener
to catch when HTTP session is destroyed.
Below is a solution that creates a log record on user login and updates the record when the user logs out or the session expires.
The log item entity:
@JmixEntity
@Table(name = "USER_AUTHENTICATION_LOG")
@Entity
public class UserAuthenticationLog {
@JmixGeneratedValue
@Column(name = "ID", nullable = false)
@Id
private UUID id;
@Column(name = "VERSION", nullable = false)
@Version
private Integer version;
@Column(name = "USERNAME", nullable = false)
@NotNull
private String username;
@Column(name = "LOGGED_IN")
private LocalDateTime loggedIn;
@Column(name = "LOGGED_OUT")
private LocalDateTime loggedOut;
@Column(name = "SESSION_ID")
private String sessionId;
// getters and setters
}
The listener bean:
@Component
public class AuthenticationEventListener implements HttpSessionListener {
@Autowired
private UnconstrainedDataManager dataManager;
@EventListener
public void onInteractiveAuthenticationSuccess(InteractiveAuthenticationSuccessEvent event) {
UserAuthenticationLog logItem = dataManager.create(UserAuthenticationLog.class);
User user = (User) event.getAuthentication().getPrincipal();
logItem.setUsername(user.getUsername());
logItem.setLoggedIn(LocalDateTime.now());
// Jmix provides session ID in ClientDetails class
logItem.setSessionId(((ClientDetails) event.getAuthentication().getDetails()).getSessionId());
dataManager.save(logItem);
}
@EventListener
public void onLogoutSuccess(LogoutSuccessEvent event) {
// we use session ID to find matching login record
String sessionId = ((ClientDetails) event.getAuthentication().getDetails()).getSessionId();
UserAuthenticationLog logItem = getLogOptional(sessionId)
.orElseGet(() -> {
UserAuthenticationLog newLogItem = dataManager.create(UserAuthenticationLog.class);
User user = (User) event.getAuthentication().getPrincipal();
newLogItem.setUsername(user.getUsername());
return newLogItem;
});
if (logItem.getLoggedOut() == null) {
logItem.setLoggedOut(LocalDateTime.now());
dataManager.save(logItem);
}
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// we use session ID to find matching login record
getLogOptional(se.getSession().getId())
.ifPresent(logItem -> {
if (logItem.getLoggedOut() == null) {
logItem.setLoggedOut(LocalDateTime.now());
dataManager.save(logItem);
}
});
}
private Optional<UserAuthenticationLog> getLogOptional(String sessionId) {
return dataManager.load(UserAuthenticationLog.class)
.query("e.sessionId = ?1", sessionId)
.optional();
}
}
To test the session expiration in UI, use the following property:
# 2 minutes
jmix.ui.http-session-expiration-timeout-sec = 120
Regards,
Konstantin