-->

如果JSF页面被j_security_check保护对Ajax请求ViewExpiredExcept

2019-06-18 05:18发布

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

  1. 在浏览器中打开JSF页面。
  2. 重新启动服务器。
  3. 点击JSF页面上的命令按钮来启动Ajax调用。

萤火显示, ViewExpiredException上升,符合市场预期。

帖子:

 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 

响应:

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

谁能帮我想出解决办法? 我希望它抛出一个异常,所以我可以处理该异常,并显示一个错误页面。 现在,它只是一个新的ViewState的回应,我的网页只是卡住,没有任何视觉反馈。

Answer 1:

我能够重现你的问题。 这到底是怎么发生的是,容器调用RequestDispatcher#forward()在安全约束中指定的登录页面。 但是,如果登录页面本身就是一个JSF页面为好,那么FacesServlet将被调用以及所转发的请求。 由于请求是前锋,这会简单地创建所转发的资源(登录页面)的新观点。 然而,因为它是一个Ajax请求,并没有render信息(整个POST请求在安全检查过程中向前基本上丢弃),只有视图状态将被退回。

请注意,如果登录页面不是一个JSF页面(例如JSP或纯HTML),则AJAX请求将返回的页面作为Ajax响应其是由JSF AJAX不可解析的整个HTML输出和解释为“空”的响应。

这是不幸的是,工作“作为设计的”。 我猜想,有一个在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 1.2被内置到OmniPartialViewContext 。 所以,如果你碰巧已经使用OmniFaces,那么这个问题是完全透明的解决,你并不需要一个定制PhaseListener这一点。



文章来源: ViewExpiredException not thrown on ajax request if JSF page is protected by j_security_check