如果JSF页面受j_security_check保护,则不会在Ajax请求上抛出ViewExpiredException

我有一个不受j_security_check保护的JSF页面。 我执行以下步骤:

  1. 在浏览器中打开JSF页面。
  2. 重新启动服务器。
  3. 单击JSF页面上的命令button以启动ajax调用。

如预期的那样,Firebug显示引发了一个ViewExpiredException

post:

 javax.faces.ViewState=8887124636062606698:-1513851009188353364 

响应:

 <partial-response> <error> <error-name>class javax.faces.application.ViewExpiredException</error-name> <error-message>viewId:/viewer.xhtml - View /viewer.xhtml could not be restored.</error-message> </error> </partial-response> 

但是,一旦我configuration页面被j_security_check保护并执行上面列出的相同步骤,奇怪(对我来说) ViewExpiredException不再被引发。 相反,响应只是一个新的视图状态。

post:

 javax.faces.ViewState=-4873187770744721574:8069938124611303615 

响应:

 <partial-response> <changes> <update id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update> </changes> </partial-response> 

有人可以帮我解决这个问题吗? 我期望它会引发一个exception,所以我可以处理这个exception并显示一个错误页面。 现在,它只是响应一个新的ViewState,我的网页刚刚卡住,没有任何视觉反馈。

我能够重现你的问题。 这里发生的事情是容器调用RequestDispatcher#forward()到安全约束中指定的login页面。 但是,如果login页面本身也是JSF页面,那么也会在转发的请求上调用FacesServlet 。 由于请求是一个转发,这将简单地在转发资源(login页面)上创build一个新的视图。 但是,由于这是一个Ajax请求,并且没有render信息(整个POST请求在安全检查期间基本上被丢弃),所以只返回视图状态。

请注意,如果login页面不是JSF页面(例如JSP或纯HTML),那么ajax请求将返回页面的整个HTML输出作为ajax响应,这是JSF ajax无法parsing的,并被解释为“空”响应。

不幸的是,“按照devise”工作。 我怀疑在JSF规范中对于Ajax请求的安全约束检查存在一些疏漏。 原因毕竟是可以理解的,幸好容易解决。 只是,实际上你不想在这里显示一个错误页面,而只是完整的login页面,就像在非ajax请求期间发生的一样。 你只需要检查当前的请求是否是一个Ajax请求,并被转发到login页面,那么你需要发送一个特殊的“redirect”ajax响应,以便整个视图将被改变。

您可以使用PhaseListener来实现这一点,如下所示:

 public class AjaxLoginListener implements PhaseListener { @Override public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } @Override public void beforePhase(PhaseEvent event) { // NOOP. } @Override public void afterPhase(PhaseEvent event) { FacesContext context = event.getFacesContext(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); String originalURL = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); String loginURL = request.getContextPath() + "/login.xhtml"; if (context.getPartialViewContext().isAjaxRequest() && originalURL != null && loginURL.equals(request.getRequestURI())) { try { context.getExternalContext().invalidateSession(); context.getExternalContext().redirect(originalURL); } catch (IOException e) { throw new FacesException(e); } } } } 

更新这个解决scheme是因为OmniFaces 1.2被内置到OmniPartialViewContext 。 所以,如果您碰巧已经使用了OmniFaces,那么这个问题就完全透明地解决了,您不需要为此定制一个PhaseListener