Окончание сеанса на стороне сервера с помощью веб-сокетов для перенаправления - PullRequest
0 голосов
/ 12 февраля 2020

Я пытаюсь настроить время ожидания сеанса на стороне сервера для прослушивателей, которые будут использовать веб-сокеты для немедленного перенаправления пользователя на страницу входа в систему после отображения ему сообщения. Я хочу, чтобы перенаправление было немедленным, а не только когда они пытаются 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);
        }
    }

    ...
}

Я не могу обработать перенаправление и обмен сообщениями, когда сокет закрыт, потому что сокеты закрываются каждый раз, когда пользователь переходит на новую страницу. Мое намерение состоит в том, чтобы поддерживать постоянную линию связи между сервером и пользователем, чтобы сообщения могли отправляться, действия могли вызываться сервером, а администраторы могли принудительно выходить из системы, одновременно информируя пользователя о причинах выхода из системы. Это работает в любом другом контексте, для которого я настроил это. Тайм-ауты сессий - единственное, что вызывает у меня проблемы.

Просто чтобы уточнить, тайм-аут работает. Сеанс признан недействительным, и если пользователь пытается перейти куда-либо, он возвращается на страницу входа. Моя цель состоит в том, чтобы принудительно перенаправить по истечении сеанса на сервере, чтобы пользователь не пытался завершить любую работу на своей странице, а затем не понимал, почему его работа была потеряна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...