如果JSF页面受j_安全检查保护,则不会在ajax请求上引发ViewExpiredException
我有一个JSF页面不受如果JSF页面受j_安全检查保护,则不会在ajax请求上引发ViewExpiredException,ajax,jsf-2,j-security-check,viewexpiredexception,Ajax,Jsf 2,J Security Check,Viewexpiredexception,我有一个JSF页面不受j\u security\u check的保护。我执行以下步骤: 在浏览器中打开JSF页面 重新启动服务器 单击JSF页面上的命令按钮启动ajax调用 Firebug显示如预期的那样引发了一个ViewExpiredException 职位: javax.faces.ViewState=8887124636062606698:-1513851009188353364 javax.faces.ViewState=-4873187770744721574:80699381246
j\u security\u check
的保护。我执行以下步骤:
ViewExpiredException
职位:
javax.faces.ViewState=8887124636062606698:-1513851009188353364
javax.faces.ViewState=-4873187770744721574:8069938124611303615
答复:
<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>
<partial-response>
<changes>
<update id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update>
</changes>
</partial-response>
答复:
<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>
<partial-response>
<changes>
<update id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update>
</changes>
</partial-response>
234065619769382809:-4498953143834600826
有人能帮我弄清楚吗?我希望它引发一个异常,这样我就可以处理该异常并显示一个错误页面。现在它只是用一个新的ViewState进行响应,我的页面只是被卡住了,没有任何视觉反馈。我能够重现您的问题。这里发生的情况是,容器调用了一个
RequestDispatcher#forward()
,以访问安全约束中指定的登录页面。但是,如果登录页面本身也是一个JSF页面,那么转发的请求也会调用FacesServlet
。由于请求是转发,这将在转发的资源(登录页面)上创建一个新视图。但是,由于这是一个ajax请求,并且没有render
信息(整个POST请求基本上在安全性向前检查期间被丢弃),因此只会返回视图状态
请注意,如果登录页面不是JSF页面(例如JSP或普通HTML),那么ajax请求会将页面的整个HTML输出作为ajax响应返回,这是JSF ajax无法解析的,并被解释为“空”响应
不幸的是,它正在“按设计”工作。我怀疑JSF规范在ajax请求的安全约束检查方面存在一些疏忽。原因毕竟是可以理解的,幸运的是很容易解决。只是,您实际上不希望在这里显示错误页面,而是只显示整个登录页面,就像在非ajax请求期间一样。您只需检查当前请求是否是ajax请求并已转发到登录页面,然后需要发送一个特殊的“重定向”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);
}
}
}
}
更新此解决方案已内置到中。因此,如果您碰巧已经使用了OmniFaces,那么这个问题就解决了,您不需要为此定制
PhaseListener
。谢谢您,巴卢斯克。保存了我的一天。哦,还有一个问题,我想知道为什么会发生以下情况:如果我使用context.getExternalContext().redirect(loginURL),它确实会将我重定向到登录页面。但在登录后,浏览器会显示一个xml文件,其内容为ViewState。xml文件与第二个xml文件I完全相同 张贴在我的问题。如果我使用context.getExternalContext().redirect(homepageURL),一切正常。它将带我进入登录页面。登录后,将显示主页。没错,这是另一个棘手的问题:容器管理的身份验证会记住所有请求参数(包括无效的javax.faces.ViewState
和另一个指示它是ajax请求的请求参数)并在成功登录后将其重新传递,这将导致一个作为ajax响应的ViewExpiredException
错误页面。这可以通过重定向到转发URI来避免,而转发URI反过来应该通过普通GET请求而不是ajax POST请求重新触发安全检查。我已经相应地更新了答案。@BalusC OmniPartialViewContext对我没有任何帮助。Ajax发布到安全页面会导致403错误(而不是viewExpiredException),jsf Ajax处理程序会忽略该错误,因此不会发生任何事情。在JSF中处理过期视图时,似乎不可能在服务器端执行任何操作。没有调用过滤器、错误处理程序或任何东西。有什么建议吗?thx的解决方案。这张支票是原版的吗=需要空值吗?对于我的web应用程序,request.getAttribute(RequestDispatcher.FORWARD\u request\u URI)始终为空