Я пытаюсь настроить время ожидания сеанса на стороне сервера для прослушивателей, которые будут использовать веб-сокеты для немедленного перенаправления пользователя на страницу входа в систему после отображения ему сообщения. Я хочу, чтобы перенаправление было немедленным, а не только когда они пытаются go перейти на новую страницу.
После долгих отладок, чтобы определить источник моей проблемы, я обнаружил, что Tomcat закрывает сеансы websocket перед закрытием сеанса приложения. Класс org. apache .catalina.session.StandardSession даже прямо заявляет об этом в методе expire, поскольку все слушатели выполняются в обратном порядке:
public void expire(boolean notify) {
...
// Notify interested application event listeners
// FIXME - Assumes we call listeners in reverse order
Context context = manager.getContext();
// The call to expire() may not have been triggered by the webapp.
// Make sure the webapp's class loader is set when calling the
// listeners
if (notify) {
ClassLoader oldContextClassLoader = null;
try {
oldContextClassLoader = context.bind(Globals.IS_SECURITY_ENABLED, null);
Object listeners[] = context.getApplicationLifecycleListeners();
if (listeners != null && listeners.length > 0) {
HttpSessionEvent event = new HttpSessionEvent(getSession());
for (int i = 0; i < listeners.length; i++) {
int j = (listeners.length - 1) - i;
if (!(listeners[j] instanceof HttpSessionListener))
continue;
...
}
Есть ли способ заставить Слушатели сеанса websocket будут запущены ПОСЛЕ моего слушателя приложения? Я попытался добавить org. apache .tomcat.websocket.server.WsSessionListener в качестве класса слушателя в мою сеть. xml, но не удалось инициализировать, так что это не go.
Мой код выглядит следующим образом.
В моей сети. xml, у меня настроено время ожидания сеанса, и я добавил Spring HttpSessionEventPublisher в качестве прослушивателя:
...
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
...
<session-config>
<session-timeout>25</session-timeout>
</session-config>
И я создал Компонент ApplicationListener, который должен был обрабатывать перенаправление пользователей:
@Component
public class SessionTimeoutListener implements ApplicationListener<SessionDestroyedEvent> {
@Override
public void onApplicationEvent(SessionDestroyedEvent event)
{
try {
List<SecurityContext> lstSecurityContext = event.getSecurityContexts();
for (SecurityContext securityContext : lstSecurityContext) {
if (securityContext != null && securityContext.getAuthentication() != null && securityContext.getAuthentication().getPrincipal() != null);
Users user = (Users)securityContext.getAuthentication().getPrincipal();
/*
* Gets the ActiveUserTracker bean, which is the websocket endpoint. Redirects the
* user to the login page after delivering a message to them.
*
* SpringUtility is just an implementation of ApplicationContextAware to get beans from the application context.
*/
ActiveUserTracker aut = SpringUtility.getBean(ActiveUserTracker.class);
aut.forceRedirect(user, "/login", "Your session has timed out. You are being redirected to the login page.");
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
И мой менеджер веб-сокетов. @OnClose здесь вызывается организацией. apache .catalina.session.StandardSession перед моей реализацией ApplicationListener, что приводит к тому, что мой код не может общаться и перенаправлять пользователя.
@Component
@ServerEndpoint(value = "/myApp/activeUser", configurator = SpringConfigurator.class)
public class ActiveUserTracker {
/**
* Tracks all of the sessions for connected users via the user id.
*/
private Map<Integer, Session> userSessions = new HashMap<Integer, Session>();
/**
* Handles the opening of new sessions. Adds the session to the userSessions map.
*/
@OnOpen
public void onOpen(Session session) throws IOException {
Object userPrincipal = session.getUserPrincipal();
if (userPrincipal != null) {
Users user = (Users)((UsernamePasswordAuthenticationToken)userPrincipal).getPrincipal();
userSessions.put(user.getId(), session);
}
else {
session.close();
}
}
/**
* Handles the closing of a session.
*/
@OnClose
public void onClose(Session session) throws IOException {
Object userPrincipal = session.getUserPrincipal();
if (userPrincipal != null) {
Users user = (Users)((UsernamePasswordAuthenticationToken)userPrincipal).getPrincipal();
if (userSessions.get(user.getId()) != null && userSessions.get(user.getId()).getId().equalsIgnoreCase(session.getId())) {
userSessions.remove(user.getId());
}
}
}
...
/**
* Forces the user to be redirected to the given URI. If a message is provided, the user will be shown the message prior to their redirection. If no message is provided, they will
* instead be immediately directed.
*
* Javascript processes the received JSON and performs the appropriate task client-side.
*
* @param user - The user whose active session will be redirected.
* @param uri - The URI to redirect the user to.
* @param message - Optional. The message to display to the user prior to redirection. If null or empty, then the redirection is immediate.
* @throws IOException
*/
public void forceRedirect(Users user, String uri, String message) throws IOException {
Session session = userSessions.get(user.getId());
if (session != null) {
RedirectionActionDto redirectionAction = new RedirectionActionDto(uri, message);
if (message == null || message.trim().isEmpty()) {
redirectionAction.setForceImmediate(true);
}
String redirectionActionJson = new Gson().toJson(redirectionAction);
session.getBasicRemote().sendText(redirectionActionJson);
}
}
...
}
Я не могу обработать перенаправление и обмен сообщениями, когда сокет закрыт, потому что сокеты закрываются каждый раз, когда пользователь переходит на новую страницу. Мое намерение состоит в том, чтобы поддерживать постоянную линию связи между сервером и пользователем, чтобы сообщения могли отправляться, действия могли вызываться сервером, а администраторы могли принудительно выходить из системы, одновременно информируя пользователя о причинах выхода из системы. Это работает в любом другом контексте, для которого я настроил это. Тайм-ауты сессий - единственное, что вызывает у меня проблемы.
Просто чтобы уточнить, тайм-аут работает. Сеанс признан недействительным, и если пользователь пытается перейти куда-либо, он возвращается на страницу входа. Моя цель состоит в том, чтобы принудительно перенаправить по истечении сеанса на сервере, чтобы пользователь не пытался завершить любую работу на своей странице, а затем не понимал, почему его работа была потеряна.