我有一个JSF页面,不受的保护j_security_check。我执行以下步骤:
j_security_check
Firebug ViewExpiredException如预期的那样显示了a 的增加。
ViewExpiredException
发布: javax.faces.ViewState=8887124636062606698:-1513851009188353364 响应:
发布:
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>
但是,一旦我将页面配置为受其保护j_security_check并执行上面列出的相同步骤,(对我而言)奇怪的ViewExpiredException是,该页面不再出现。相反,响应只是一个新的视图状态。
发布: javax.faces.ViewState=-4873187770744721574:8069938124611303615 响应:
javax.faces.ViewState=-4873187770744721574:8069938124611303615
> <partial-response> > <changes> > <update > id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update> > </changes> > </partial-response>
有人可以帮我解决这个问题吗?我希望它会引发异常,因此我可以处理该异常并显示错误页面。现在它只是以新的ViewState进行响应,我的页面被卡住了,而没有任何视觉反馈。
我能够重现您的问题。这里发生的是容器RequestDispatcher#forward()按照安全性约束中的指定调用登录页面。但是,如果登录页面本身也就是JSF页面,则FacesServlet还将在转发的请求上调用。由于请求是转发的,因此只会在转发的资源(登录页面)上创建一个新视图。但是,由于这是一个ajax请求,并且没有任何render信息(整个POST请求在安全检查转发过程中基本上被丢弃),因此仅返回视图状态。
RequestDispatcher#forward()
FacesServlet
render
请注意,如果登录页面不是JSF页面(例如JSP或纯HTML),那么ajax请求将返回该页面的整个HTML输出作为ajax响应,这是JSF ajax无法解析的,并被解释为“空”响应。
不幸的是,它是“按设计的”方式工作的。我怀疑JSF规范中对ajax请求的安全性约束检查有一些疏忽。毕竟,原因是可以理解的,而且很容易解决。只是,您实际上并不希望在此处显示错误页面,而是仅显示整个登录页面,这与非ajax请求期间的情况完全相同。您只需要检查当前请求是否为ajax请求并将其转发到登录页面,则需要发送特殊的“重定向” ajax响应,以便更改整个视图。
您可以通过以下方法实现此目的PhaseListener:
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); } } } }
更新 此解决方案是因为OmniFaces 1.2已内置到OmniPartialViewContext。因此,如果您碰巧已经使用过OmniFaces,则可以完全透明地解决此问题,并且不需要PhaseListener为此自定义。
OmniPartialViewContext