Автоматически выполнять действия на стороне клиента по окончании сеанса

Я хочу показать в <p:growl>, что сеанс истек. Я нашел много методов для обработки истечения сеанса, таких как Тайм-аут сеанса и обработка ViewExpiredException в JSF/PrimeFaces ajax-запросе, но я не смог передать сообщение лица <p:growl>.

Насколько я могу автоматически запустить некоторый (JavaScript) код на стороне клиента, когда сеанс HTTP автоматически истекает на стороне сервера?

Ответ 1

Для этого вы можете использовать монитор простоя PrimeFaces. Пользователь перенаправляется на выход из системы после истечения времени ожидания для аннулирования сеанса. За 2 минуты до отображения обратного отсчета, чтобы предупредить пользователя. После перемещения мыши снова сеанс продлен.

Монитор простоя PrimeFaces и диалог помещены в шаблон, который вы можете добавить на каждую вовлеченную страницу:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   xmlns:p="http://primefaces.org/ui">

<ui:composition>
   <h:form prependId="false">
      <p:idleMonitor
     timeout="#{session.maxInactiveInterval * 1000 - 125000}"
     onidle="startIdleMonitor()"
     onactive="timeoutDialog.hide()" />

      <p:dialog id="timeoutSession"
     header="#{msg['session.expire']}"
     widgetVar="timeoutDialog"
     showEffect="fade" hideEffect="fade"
     modal="true"
     width="400"
     height="110"
     closable="false"
     draggable="false"
     resizable="false"
     appendToBody="true"
     onHide="stopCount()"
     onShow="doTimer()">
     <br />
     <p>
        <span class="ui-icon ui-icon-alert" style="float: left; margin: 8px 8px 0;"/>
        <p:panel>
           #{msg['logoff.soon.1']}
           <span id="dialog-countdown" style="font-weight: bold"></span>
           #{msg['logoff.soon.2']}
        </p:panel>
     </p>
     <br />
     <p style="font-weight: bold;">#{msg['move.cursor']}</p>
      </p:dialog>
      <p:remoteCommand name="keepAlive" actionListener="#{auth.keepSessionAlive}" />
   </h:form>
   <script type="text/javascript">
      var TIME = 120; // in seconds
      var countTimer = TIME;
      var processTimer;
      var timer_is_on = 0;
      var redirectPage = "#{request.contextPath}/auth/j_verinice_timeout";

      var countDownDiv = "dialog-countdown";
      var txtCountDown = null;
      if (!txtCountDown)
        txtCountDown = document.getElementById(countDownDiv);

      function startIdleMonitor() {
        countTimer = TIME;
        txtCountDown.innerHTML = countTimer;
        timeoutDialog.show();
      }
      function timedCount() {
        txtCountDown.innerHTML = countTimer;
        if (countTimer == 0) {
            stopCount();
            window.location.href = redirectPage;
            return;
        }
        countTimer = countTimer - 1;
        processTimer = setTimeout("timedCount()", 1000);
      }
      function doTimer() {
        if (!timer_is_on) {
            timer_is_on = 1;
            timedCount();
        }
      }
      function stopCount() {
        clearTimeout(processTimer);
        timer_is_on = 0;
        keepAlive();
      }
      </script>
</ui:composition>
</html>
  •   Строка 11: время ожидания простоя монитора устанавливается системной переменной session.maxInactiveInterval. Значение, которое вы указали в файле web.xml или в конфигурации сервера.
  •   Строка 12/13: Javascript-метод startIdleMonitor() вызывается после тайм-аута без какого-либо взаимодействия с пользователем. Этот метод открывает диалог. timeoutDialog.hide() вызывается, когда пользователь снова занят: диалог закрыт
  •   Строка 26/27: при показе или скрытии диалога вызываются еще два метода Javascript: doTimer() запускается, а stopCount() останавливает обратный отсчет.
  •   Строка 40: удаленная команда PrimeFaces, чтобы сохранить сессию живой. Вызывая произвольный метод на сервере, сеанс расширяется. Команда вызывается методом Javascript keepAlive() в строке 78.
  •   Строка 59-68: метод Javascript timedCount() вызывается каждую секунду для выполнения обратного отсчета. После тайм-аута перенаправление выполняется в строке 63.

Чтобы активировать обработку тайм-аута на нескольких страницах, включите шаблон тайм-аута в шаблон макета:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xml:lang="de-DE">
<h:head>
  ...
</h:head>
<body>
  <ui:include src="/template/sessionTimeOut.xhtml" />
  <ui:include src="/nav.xhtml"/>>
  <ui:insert name="content">Default content</ui:insert>
  <ui:include src="/footer.xhtml"/>>
</body>
</html>

Определенное время ожидания для вашего веб-приложения вы можете установить в своем файле web.xml:

<!--?xml version="1.0" encoding="UTF-8"?-->
<web-app>
   ...
   <session-config>
      <!-- Session idle timeout in min. -->
      <session-timeout>30</session-timeout>
    </session-config>
</web-app>

Подробнее об этом решении вы можете прочитать в этой записи блога: JSF и PrimeFaces: обработка тайм-аута сеанса

Ответ 2

Если вы используете JSF 2.3, вы можете использовать для этого сеанс <f:websocket>. Когда сеанс истекает на стороне сервера, тогда <f:websocket> автоматически закрывается закрыть код 1000 ( "нормальное закрытие" ).

Другими словами, просто выполните:

<f:websocket ... scope="session" onclose="sessionScopedSocketCloseListener" />

function sessionScopedSocketCloseListener(code) {
    if (code == 1000) {
        alert("Session has expired!");
    }
}

В случае необходимости вы можете объединить это с JavaScript API <p:growl>.

<p:growl widgetVar="growl" ... />

PF("growl").renderMessage({severity: "warn", summary: "Session has expired!" });

Если вы еще не на JSF 2.3, вы всегда можете использовать <o:socket>, который предлагает точно такую ​​же функцию onclose как <f:websocket>. См. Также Как сервер нажимает асинхронные изменения на HTML-страницу, созданную JSF?

Ответ 3

Код, добавленный @uı6ʎɹnɯ lǝıuɐp, очень полезен, но я добавлю несколько замечаний:

  • @Jonathan L sugests заменяют processTimer = setTimeout("timedCount()", 1000) на setInterval("timedCount()", 1000). Я думаю, имеет смысл, но нужно немного модификации:

    function doTimer() {
        if (!timer_is_on) {
            timer_is_on = 1;
            processTimer = setInterval("timedCount()", 1000);
        }
    }
    
    function stopCount() {
        clearInterval(processTimer);
        timer_is_on = 0;
        keepAlive();
    }
    

  • Метод timedCount() можно изменить на:

    function timedCount() {
        txtCountDown.innerHTML = countTimer;
        if (countTimer > 0) {
            countTimer = countTimer - 1;
        } else {
            clearInterval(processTimer);
            doLogout();
        }
    }
    

    И вам нужно добавить под keepAlive remoteCommand что-то вроде этого:

    <p:remoteCommand name="doLogout" action="#{loginMB.logout()}" 
                     process="@this" partialSubmit="true" immediate="true" />
    

    Внутри управляемого Bean:

    public void logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        validUser = false;
        loggedUser = false;
        redirectToPage("/login.xhtml");
    }
    
    private void redirectToPage(String page) {
        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
        context.redirect(context.getRequestContextPath() + page);
    }
    

    Помните, что атрибут name="doLogout" p:remoteCommand преобразуется в функцию javascript, которую вы можете вызвать в любом месте. Он идеально подходит для итерации представления с помощью управляемого bean.

    Этот подход предотвратит использование вашей системы ViewExpiredException.


  • Некоторые люди не понимают концепцию keepAlive, но это очень просто, просто добавьте простую функцию внутри вашего управляемого bean. Пример keepSessionAlive:

    public void keepSessionAlive () {
        System.out.println(">>> Session is alive... ");
    }
    

    Помните, что p:remoteCommand с именем keepAlive представляет собой функцию javascript под названием keepAlive() и вызывает действие keepSessionAlive внутри вашего управляемого bean.

    Это действие означает, что вы не праздны.


  • session.maxInactiveInterval ссылок idleMonitor <session-timeout> web.xml

    <p:idleMonitor timeout="#{session.maxInactiveInterval * 1000 - 125000}"
                   onidle="startIdleMonitor()" onactive="timeoutDialog.hide()" />
    

    Я рекомендую поставить монитор сеанса на выполнение за 5 секунд до завершения сеанса, чтобы предотвратить запуск doLogout() после завершения сеанса и получить ViewExpiredException.

    Первая строка кода javascript var TIME = 120; // in seconds, это время завершения сеанса. Исходный код # {session.maxInactiveInterval * 1000 - 125000} будет использовать сессионный тайм-аут, умноженный на 1.000 (потому что он miliseconds) и вычитает 125.000, что составляет 125 секунд, на 5 секунд меньше, чем счетчик, поэтому не нуждается в изменениях.


Надеюсь, это поможет... Удачи!

Ответ 4

Я просто хотел опубликовать здесь для будущих посетителей, я смог найти немного другой, но рабочий подход с компонентами idlemonitor и timer. Как правило, если в web.xml время ожидания сеанса установлено равным 30 минутам, этот код откроет диалоговое окно через 28 минут простоя пользователя со следующими компонентами: Сообщение - Вы собираетесь выйти из системы через минут. Пожалуйста, нажмите кнопку ОК, чтобы сохранить сеанс. Таймер - таймер расширений Primefaces, который будет иметь 2-минутный обратный отсчет. Кнопка ОК - поддерживает сессию Кнопка "Выйти" - выход пользователя из системы. Вот код для этого изменения:

sessionTimeOut.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
       xmlns:f="http://java.sun.com/jsf/core"
       xmlns:h="http://java.sun.com/jsf/html"
       xmlns:ui="http://java.sun.com/jsf/facelets"
       xmlns:p="http://primefaces.org/ui"
       xmlns:pu="http://primefaces.org/ultima"
       xmlns:pe="http://primefaces.org/ui/extensions">


       <h:form>
           <p:idleMonitor timeout="#{session.maxInactiveInterval * 1000 - 130000}" onidle="PF('idleDialog').show();PF('timeoutTimer').start();" />

           <p:dialog id="timeoutDialog" header="Are you there?" widgetVar="idleDialog" modal="true" closable="false" draggable="false" resizable="false" >
                <p:panelGrid columns="1" styleClass="ui-noborder">

                    <p:panel>
                        <h:outputText value="You are about to be logged off in " />
                        <p:spacer width="10"/>
                        <pe:timer id="timeoutTimer" widgetVar="timeoutTimer" singleRun="true" timeout="120" format="mm:ss" autoStart="false" listener="#{userSessionBean.logout()}"/>
                        <p:spacer width="10"/>
                        <h:outputText value=" mins." />

                    </p:panel>

                    <p:panel>
                        <h:outputText value="Please click 'Ok' to keep the session alive" />
                    </p:panel>

                    <p:panel style="text-align: center;">
                        <p:commandButton id="confirm" value="Ok" actionListener="#{userSessionBean.keepAlive()}" onclick="PF('timeoutTimer').stop(true);" oncomplete="PF('idleDialog').hide();" process="@this"/>
                        <p:spacer width="10"/>
                        <p:commandButton id="timeoutLogout" value="Log Out" widgetVar="timeoutLogoutWV"
                               actionListener="#{userSessionBean.logout()}" oncomplete="PF('timeoutTimer').stop(true); PF('idleDialog').hide(); location.reload(true);"/>
                    </p:panel>

                </p:panelGrid>
           </p:dialog>
       </h:form>

</ui:composition>

вставьте это в свой базовый шаблон, как этот

<ui:include src="./sessionTimeOut.xhtml" />

Вот код Java-стороны

public void keepAlive() {
    logger.info("User " + loggedInUser.getUserLogin() + " requested the Session " + getCurrentHttpSessionId() + "  to be kept alive at " + new Date().toString());

    /**
     * Do nothing
     */
}


public String logout() throws IOException {
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    externalContext.getContextName();

    logger.info(FacesContext.getCurrentInstance().getExternalContext().getContextName());

    facesContext.getExternalContext().redirect("/Project/views/login.xhtml");

    logger.info("Logout");

}

Надеюсь, это поможет. Благодарю.