Тайм-аут сеанса и обработка ViewExpiredException в JSF/PrimeFaces ajax request
Я нахожу эту статью полезной для запроса non-ajax Как обрабатывать завершение сеанса и ViewExpiredException в JSF 2?
но я не могу использовать это, когда я отправляю вызов AJAX.
Предположим, что в диалоговом окне перфолей я делаю post-запрос с использованием AJAX, и сеанс уже отключен.
Я вижу, что моя страница застревает.
Как исправить такой сценарий, чтобы при публикации AJAX я мог перенаправить его на страницу с истекшим временем просмотра и
затем переместите его на страницу входа, аналогичную решению по ссылке выше?
JSF2/Primefaces/Glassfish
Ответы
Ответ 1
Исключения, которые генерируются во время запросов ajax, по умолчанию полностью не поддерживают обратную связь на стороне клиента. Только когда вы запускаете Mojarra с этапом проекта, установленным на Development
, и используйте <f:ajax>
, тогда вы получите голый оповещение JavaScript с типом исключения и сообщением. Но кроме этого, и в PrimeFaces, по умолчанию нет обратной связи. Однако вы можете увидеть исключение в журнале сервера и в ответе ajax (в разделе "Сетевой раздел" на сайте разработчика веб-браузера).
Вам нужно реализовать пользовательский ExceptionHandler
, который выполняет в основном следующее задание, когда в очереди находится ViewExpiredException
:
String errorPageLocation = "/WEB-INF/errorpages/expired.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
В качестве альтернативы вы можете использовать служебную библиотеку JSF OmniFaces. Он имеет FullAjaxExceptionHandler
именно для этой цели (исходный код здесь, демонстрация демонстрации здесь).
См. также:
Ответ 2
Слияние между ответом @BalusC и этот пост, я решил свою проблему!
My ExceptionHandlerWrapper:
public class CustomExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
CustomExceptionHandler(ExceptionHandler exception) {
this.wrapped = exception;
}
@Override
public ExceptionHandler getWrapped() {
return wrapped;
}
@Override
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
while (i.hasNext()) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context
= (ExceptionQueuedEventContext) event.getSource();
// get the exception from context
Throwable t = context.getException();
final FacesContext fc = FacesContext.getCurrentInstance();
final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
final NavigationHandler nav = fc.getApplication().getNavigationHandler();
//here you do what ever you want with exception
try {
//log error ?
//log.log(Level.SEVERE, "Critical Exception!", t);
if (t instanceof ViewExpiredException) {
requestMap.put("javax.servlet.error.message", "Session expired, try again!");
String errorPageLocation = "/erro.xhtml";
fc.setViewRoot(fc.getApplication().getViewHandler().createView(fc, errorPageLocation));
fc.getPartialViewContext().setRenderAll(true);
fc.renderResponse();
} else {
//redirect error page
requestMap.put("javax.servlet.error.message", t.getMessage());
nav.handleNavigation(fc, null, "/erro.xhtml");
}
fc.renderResponse();
// remove the comment below if you want to report the error in a jsf error message
//JsfUtil.addErrorMessage(t.getMessage());
} finally {
//remove it from queue
i.remove();
}
}
//parent hanle
getWrapped().handle();
}
}
My ExceptionHandlerFactory:
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
// this injection handles jsf
public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
@Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler handler = new CustomExceptionHandler(parent.getExceptionHandler());
return handler;
}
}
Мои лица-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<factory>
<exception-handler-factory>
your.package.here.CustomExceptionHandlerFactory
</exception-handler-factory>
</factory>
</faces-config>
Ответ 3
Я использую Mojarra 2.1.7 в режиме Production с JBoss 7. По окончании сеанса AJAX вызывает возврат XML-документа с ошибкой. Вы можете легко поймать эту ошибку, используя обычный обработчик onerror f: ajax.
<script type="text/javascript">
function showError(data) {
alert("An error happened");
console.log(data);
}
</script>
<h:commandLink action="...">
<f:ajax execute="..." render="..." onerror="showError"/>
</h:commandLink>
Ответ 4
Я включил это в мой класс ViewExpiredExceptionHandler, и он отлично работал у меня в WAS
public void handle() throws FacesException {
FacesContext facesContext = FacesContext.getCurrentInstance();
for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents()
.iterator(); iter.hasNext();) {
Throwable exception = iter.next().getContext().getException();
if (exception instanceof ViewExpiredException) {
final ExternalContext externalContext = facesContext
.getExternalContext();
try {
facesContext.setViewRoot(facesContext.getApplication()
.getViewHandler()
.createView(facesContext, "/Login.xhtml")); //Login.xhtml is the page to to be viewed. Better not to give /WEB-INF/Login.xhtml
externalContext.redirect("ibm_security_logout?logoutExitPage=/Login.xhtml"); // when browser back button is pressed after session timeout, I used this.
facesContext.getPartialViewContext().setRenderAll(true);
facesContext.renderResponse();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
iter.remove();
}
}
}
getWrapped().handle();
}
Надеюсь, что это поможет
Ответ 5
Я столкнулся с этой проблемой, Требование должно отображать всплывающее окно подтверждения, когда пользователь делает какое-либо действие после истечения времени ожидания сеанса, мое предлагаемое решение:
<security:http use-expressions="true" auto-config="true" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/common/auth/**" access="permitAll" />
<security:intercept-url pattern="/javax.faces.resource/**" access="permitAll" />
<security:intercept-url pattern="/**/ *.*" access="hasRole('ROLE_ADMIN')" />
<security:form-login login-page="/common/auth/login.jsf" />
<!-- <security:remember-me key="secret" services-ref="rememberMeServices" /> -->
<security:logout invalidate-session="true" logout-success-url="/common/auth/login.jsf" />
</security:http>
<bean id="authenticationEntryPoint" class="com.x.y.MyRedirectEntryPoint" >
<property name="loginFormUrl" value="/common/auth/login.jsf"/>
</bean>
MyRedirectEntryPoint должен расширять AuthenticationProcessingFilterEntryPoint и отменять метод начала
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
boolean ajaxRedirect = request.getHeader("faces-request") != null
&& request.getHeader("faces-request").toLowerCase().indexOf("ajax") > -1;
if (ajaxRedirect) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
response.sendError(403);
}
} else {
super.commence(request, response, authException);
}
}
Теперь вы можете просто привязать функцию обратного вызова javascript, чтобы поймать заброшенную ошибку 403 и сделать то, что вы хотите:
$(document).bind('ajaxError',
function(event, request, settings, exception){
if (request.status==403){
//do whatever you wanted may be show a popup or just redirect
window.location = '#{request.contextPath}/';
}
});