JSF / PrimeFaces ajax请求上的会话超时和ViewExpiredException处理

我发现这篇文章对于非ajax请求很有用如何在JSF 2中处理会话到期和ViewExpiredException? 但是当我使用AJAX调用提交时,我无法使用它。

假设在一个primefaces对话框中,我正在使用AJAX发送一个post请求,session已经超时了。 我看到我的页面卡住了。

如何解决这种情况,当我发布使用AJAX,我可以redirect他到我的视图过期的页面,然后转发他的login页面类似于上面的链接的解决scheme?

JSF2 / Primefaces / Glassfish的

在Ajax请求期间抛出的exception在客户端默认没有任何反馈。 只有当您将项目阶段设置为Development并运行Mojarra并使用<f:ajax> ,才会得到带有exceptiontypes和消息的纯JavaScript警报。 但除此之外,在PrimeFaces中,默认情况下根本没有任何反馈。 但是,您可以在服务器日志和ajax响应中(在Web浏览器的开发人员工具集的“networking”部分中)看到exception。

你需要实现一个自定义的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正是为了这个目的(源代码在这里 ,展示演示这里 )。

也可以看看:

  • 为什么使用JSF ExceptionHandlerFactory而不是<error-page>redirect?
  • 处理AJAX化组件的JSF 2.0exception的正确方法是什么?

@BalusC的答案和这篇文章之间的合并,我解决了我的问题!

我的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(); } } 

我的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> 

我正在使用JBoss 7的Production模式使用Mojarra 2.1.7。会话过期后,AJAX调用将返回错误的XML文档。 你可以很容易地使用f:ajax的通常的onerror处理程序来捕获这个错误。

 <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> 

我已经在我的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(); } 

希望这可以帮助

我遇到了这个问题,当用户在会话超时后做任何动作时,需要显示一个确认popup框,我提出的解决scheme是:

 <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.xyMyRedirectEntryPoint" > <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); } } 

现在,您可以简单地绑定一个callbackJavaScript函数来捕获抛出的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}/'; } });