mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 18:30:57 +00:00
logout process
This commit is contained in:
parent
79439f392f
commit
762571f42b
@ -1,5 +1,7 @@
|
|||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
@ -39,6 +41,9 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("GET".equalsIgnoreCase(request.getMethod())) {
|
if ("GET".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
|
||||||
|
Principal principal = request.getUserPrincipal();
|
||||||
|
|
||||||
if ("/".equals(request.getRequestURI())
|
if ("/".equals(request.getRequestURI())
|
||||||
|| "/login".equals(request.getRequestURI())
|
|| "/login".equals(request.getRequestURI())
|
||||||
|| "/home".equals(request.getRequestURI())
|
|| "/home".equals(request.getRequestURI())
|
||||||
@ -55,20 +60,15 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
|||||||
|| request.getRequestURI().endsWith(".webmanifest")
|
|| request.getRequestURI().endsWith(".webmanifest")
|
||||||
|| request.getRequestURI().contains("/files/")) {
|
|| request.getRequestURI().contains("/files/")) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else if (principal != null) {
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
session = request.getSession(true);
|
session = request.getSession(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
final HttpSession finalSession = session;
|
final HttpSession finalSession = session;
|
||||||
String sessionId = finalSession.getId();
|
String sessionId = finalSession.getId();
|
||||||
|
|
||||||
// Den aktuellen Benutzer (principalName) aus der Session ermitteln.
|
final String currentPrincipal = principal.getName();
|
||||||
// Es wird angenommen, dass das Attribut "principalName" in der Session gesetzt
|
|
||||||
// wurde.
|
|
||||||
final String currentPrincipal =
|
|
||||||
finalSession.getAttribute("principalName") != null
|
|
||||||
? finalSession.getAttribute("principalName").toString()
|
|
||||||
: "unknown";
|
|
||||||
|
|
||||||
// Zähle alle nicht abgelaufenen Sessions des aktuellen Benutzers.
|
// Zähle alle nicht abgelaufenen Sessions des aktuellen Benutzers.
|
||||||
long userSessions =
|
long userSessions =
|
||||||
|
@ -42,8 +42,6 @@ public class AnonymusSessionRegistry implements HttpSessionListener, SessionsInt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.setAttribute("principalName", "anonymousUser");
|
|
||||||
|
|
||||||
// Speichern des Erstellungszeitpunkts
|
// Speichern des Erstellungszeitpunkts
|
||||||
Date creationTime = new Date();
|
Date creationTime = new Date();
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class AnonymusSessionStatusController {
|
public class AnonymusSessionStatusController {
|
||||||
|
@ -36,6 +36,7 @@ import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
|||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
|
||||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
|
import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
|
||||||
|
import stirling.software.SPDF.config.security.session.PreLogoutDataCaptureHandler;
|
||||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.User;
|
import stirling.software.SPDF.model.User;
|
||||||
@ -158,6 +159,8 @@ public class SecurityConfiguration {
|
|||||||
http.logout(
|
http.logout(
|
||||||
logout ->
|
logout ->
|
||||||
logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
|
||||||
|
.addLogoutHandler(
|
||||||
|
new PreLogoutDataCaptureHandler(sessionRegistry))
|
||||||
.logoutSuccessHandler(
|
.logoutSuccessHandler(
|
||||||
new CustomLogoutSuccessHandler(applicationProperties))
|
new CustomLogoutSuccessHandler(applicationProperties))
|
||||||
.clearAuthentication(true)
|
.clearAuthentication(true)
|
||||||
|
@ -10,6 +10,7 @@ import java.util.Date;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -33,13 +34,20 @@ import stirling.software.SPDF.model.SessionEntity;
|
|||||||
public class CustomHttpSessionListener implements HttpSessionListener, SessionsInterface {
|
public class CustomHttpSessionListener implements HttpSessionListener, SessionsInterface {
|
||||||
|
|
||||||
private final SessionPersistentRegistry sessionPersistentRegistry;
|
private final SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
|
private final boolean loginEnabled;
|
||||||
|
private final boolean runningEE;
|
||||||
|
|
||||||
@Value("${server.servlet.session.timeout:120s}") // TODO: Change to 30m
|
@Value("${server.servlet.session.timeout:120s}") // TODO: Change to 30m
|
||||||
private Duration defaultMaxInactiveInterval;
|
private Duration defaultMaxInactiveInterval;
|
||||||
|
|
||||||
public CustomHttpSessionListener(SessionPersistentRegistry sessionPersistentRegistry) {
|
public CustomHttpSessionListener(
|
||||||
|
SessionPersistentRegistry sessionPersistentRegistry,
|
||||||
|
@Qualifier("loginEnabled") boolean loginEnabled,
|
||||||
|
@Qualifier("runningEE") boolean runningEE) {
|
||||||
super();
|
super();
|
||||||
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
||||||
|
this.loginEnabled = loginEnabled;
|
||||||
|
this.runningEE = runningEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -110,18 +118,46 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
|||||||
if (principalName == null) {
|
if (principalName == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session.setAttribute("principalName", principalName);
|
if ("anonymousUser".equals(principalName) && loginEnabled) {
|
||||||
if ("anonymousUser".equals(principalName)) {
|
return;
|
||||||
log.info("Principal is anonymousUser");
|
|
||||||
}
|
}
|
||||||
int allNonExpiredSessions = getAllNonExpiredSessions().size();
|
int allNonExpiredSessions = getAllNonExpiredSessions().size();
|
||||||
if (allNonExpiredSessions >= getMaxUserSessions()) {
|
|
||||||
|
allNonExpiredSessions =
|
||||||
|
getAllSessions().stream()
|
||||||
|
.filter(s -> !s.isExpired())
|
||||||
|
.filter(s -> s.getPrincipalName().equals(principalName))
|
||||||
|
.filter(s -> "anonymousUser".equals(principalName) && !loginEnabled)
|
||||||
|
.peek(s -> log.info("Session {}", s.getPrincipalName()))
|
||||||
|
.toList()
|
||||||
|
.size();
|
||||||
|
|
||||||
|
int all =
|
||||||
|
getAllSessions().stream()
|
||||||
|
.filter(s -> !s.isExpired() && s.getPrincipalName().equals(principalName))
|
||||||
|
.toList()
|
||||||
|
.size();
|
||||||
|
boolean isAnonymousUserWithoutLogin = "anonymousUser".equals(principalName) && loginEnabled;
|
||||||
|
log.info(
|
||||||
|
"all {} allNonExpiredSessions {} {} isAnonymousUserWithoutLogin {}",
|
||||||
|
all,
|
||||||
|
allNonExpiredSessions,
|
||||||
|
getMaxUserSessions(),
|
||||||
|
isAnonymousUserWithoutLogin);
|
||||||
|
|
||||||
|
if (allNonExpiredSessions >= getMaxApplicationSessions() && !isAnonymousUserWithoutLogin) {
|
||||||
log.info("Session {} Expired=TRUE", session.getId());
|
log.info("Session {} Expired=TRUE", session.getId());
|
||||||
sessionPersistentRegistry.expireSession(session.getId());
|
sessionPersistentRegistry.expireSession(session.getId());
|
||||||
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
||||||
// if (allNonExpiredSessions > getMaxUserSessions()) {
|
// if (allNonExpiredSessions > getMaxUserSessions()) {
|
||||||
// enforceMaxSessionsForPrincipal(principalName);
|
// enforceMaxSessionsForPrincipal(principalName);
|
||||||
// }
|
// }
|
||||||
|
} else if (all >= getMaxUserSessions() && !isAnonymousUserWithoutLogin) {
|
||||||
|
enforceMaxSessionsForPrincipal(principalName);
|
||||||
|
log.info("Session {} Expired=TRUE", principalName);
|
||||||
|
} else if (isAnonymousUserWithoutLogin) {
|
||||||
|
sessionPersistentRegistry.expireSession(session.getId());
|
||||||
|
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
||||||
} else {
|
} else {
|
||||||
log.info("Session created: {}", principalName);
|
log.info("Session created: {}", principalName);
|
||||||
sessionPersistentRegistry.registerNewSession(se.getSession().getId(), principalName);
|
sessionPersistentRegistry.registerNewSession(se.getSession().getId(), principalName);
|
||||||
@ -164,7 +200,6 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
|||||||
if (session == null) {
|
if (session == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionInformation sessionsInfo =
|
SessionInformation sessionsInfo =
|
||||||
sessionPersistentRegistry.getSessionInformation(session.getId());
|
sessionPersistentRegistry.getSessionInformation(session.getId());
|
||||||
if (sessionsInfo == null) {
|
if (sessionsInfo == null) {
|
||||||
@ -197,4 +232,22 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
|||||||
sessionPersistentRegistry.removeSessionInformation(session.getId());
|
sessionPersistentRegistry.removeSessionInformation(session.getId());
|
||||||
log.info("Session {} wurde Expired=TRUE", session.getId());
|
log.info("Session {} wurde Expired=TRUE", session.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the maximum number of sessions
|
||||||
|
@Override
|
||||||
|
public int getMaxApplicationSessions() {
|
||||||
|
if (runningEE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return getMaxUserSessions() * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the maximum number of user sessions
|
||||||
|
@Override
|
||||||
|
public int getMaxUserSessions() {
|
||||||
|
if (runningEE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package stirling.software.SPDF.config.security.session;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PreLogoutDataCaptureHandler implements LogoutHandler {
|
||||||
|
|
||||||
|
private final SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logout(
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
Authentication authentication) {
|
||||||
|
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sessionId = session.getId();
|
||||||
|
if (sessionId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String path = request.getServletPath();
|
||||||
|
if (path == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!"/logout".equals(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.debug("Session ID: {} Principal: {}", sessionId, authentication.getPrincipal());
|
||||||
|
sessionPersistentRegistry.expireSession(sessionId);
|
||||||
|
sessionPersistentRegistry.removeSessionInformation(sessionId);
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,7 @@ public class SessionScheduled {
|
|||||||
} else if (principal instanceof String stringPrincipal) {
|
} else if (principal instanceof String stringPrincipal) {
|
||||||
// Skip anonymousUser if login is enabled
|
// Skip anonymousUser if login is enabled
|
||||||
if ("anonymousUser".equals(stringPrincipal) && loginEnabledValue) {
|
if ("anonymousUser".equals(stringPrincipal) && loginEnabledValue) {
|
||||||
|
sessionPersistentRegistry.expireAllSessionsByPrincipalName(stringPrincipal);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,18 +50,13 @@ public class SessionScheduled {
|
|||||||
Instant expirationTime =
|
Instant expirationTime =
|
||||||
lastRequest.toInstant().plus(maxInactiveInterval, ChronoUnit.SECONDS);
|
lastRequest.toInstant().plus(maxInactiveInterval, ChronoUnit.SECONDS);
|
||||||
if (now.isAfter(expirationTime)) {
|
if (now.isAfter(expirationTime)) {
|
||||||
log.info(
|
|
||||||
"SessionID: {} expiration time: {} Current time: {}",
|
|
||||||
sessionInformation.getSessionId(),
|
|
||||||
expirationTime,
|
|
||||||
now);
|
|
||||||
sessionPersistentRegistry.expireSession(sessionInformation.getSessionId());
|
sessionPersistentRegistry.expireSession(sessionInformation.getSessionId());
|
||||||
sessionInformation.expireNow();
|
sessionInformation.expireNow();
|
||||||
if (authentication != null && principal.equals(authentication.getPrincipal())) {
|
if (authentication != null && principal.equals(authentication.getPrincipal())) {
|
||||||
authentication.setAuthenticated(false);
|
authentication.setAuthenticated(false);
|
||||||
}
|
}
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
log.info(
|
log.debug(
|
||||||
"Session expired for principal: {} SessionID: {}",
|
"Session expired for principal: {} SessionID: {}",
|
||||||
principal,
|
principal,
|
||||||
sessionInformation.getSessionId());
|
sessionInformation.getSessionId());
|
||||||
|
@ -348,7 +348,6 @@ public class AccountWebController {
|
|||||||
model.addAttribute("maxSessions", maxSessions);
|
model.addAttribute("maxSessions", maxSessions);
|
||||||
model.addAttribute("maxUserSessions", maxUserSessions);
|
model.addAttribute("maxUserSessions", maxUserSessions);
|
||||||
model.addAttribute("sessionCount", sessionCount);
|
model.addAttribute("sessionCount", sessionCount);
|
||||||
model.addAttribute("maxEnterpriseUsers", applicationProperties.getPremium().getMaxUsers());
|
|
||||||
model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers());
|
model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers());
|
||||||
return "adminSettings";
|
return "adminSettings";
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,8 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong> <span
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong>
|
||||||
th:text="${totalUsers}"></span><span th:if="${@runningProOrHigher}" th:text="'/'+${maxEnterpriseUsers}"></span>
|
<span th:text="${totalUsers}"></span>
|
||||||
<span th:if="${@runningProOrHigher}" th:text="'/'+${maxPaidUsers}"></span>
|
<span th:if="${@runningProOrHigher}" th:text="'/'+${maxPaidUsers}"></span>
|
||||||
|
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
||||||
@ -70,11 +70,13 @@
|
|||||||
<span th:text="${disabledUsers}"></span>
|
<span th:text="${disabledUsers}"></span>
|
||||||
<th:block th:if="${@runningProOrHigher}">
|
<th:block th:if="${@runningProOrHigher}">
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total
|
||||||
Sessions:</strong> <span th:text="${sessionCount}"></span>
|
Sessions:</strong>
|
||||||
|
<span th:text="${sessionCount}"></span>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block th:if="${!@runningProOrHigher}">
|
<th:block th:if="${!@runningProOrHigher}">
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total
|
||||||
Sessions:</strong> <span th:text="${sessionCount}"></span>/<span th:text="${maxSessions}"></span>
|
Sessions:</strong>
|
||||||
|
<span th:text="${sessionCount}"></span>/<span th:text="${maxSessions}"></span>
|
||||||
</th:block>
|
</th:block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user