小编典典

如果JSF页面受j_security_check保护,则不会在ajax请求上引发ViewExpiredException

ajax

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

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

Firebug ViewExpiredException如预期的那样显示了a 的增加。

发布:

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进行响应,我的页面被卡住了,而没有任何视觉反馈。


阅读 408

收藏
2020-07-26

共1个答案

小编典典

我能够重现您的问题。这里发生的是容器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
1.2
已内置到OmniPartialViewContext。因此,如果您碰巧已经使用过OmniFaces,则可以完全透明地解决此问题,并且不需要PhaseListener为此自定义。

2020-07-26