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