ViewExpiredException不是扔在Ajax请求,如果JSF页面由j_security_check保护 [英] ViewExpiredException not thrown on ajax request if JSF page is protected by j_security_check

查看:349
本文介绍了ViewExpiredException不是扔在Ajax请求,如果JSF页面由j_security_check保护的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有没有被 j_security_check 保护的JSF页面。我执行以下步骤:

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

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

  

发表

  javax.faces.ViewState = 8887124636062606698:-1513851009188353364
 

     

响应:

 <部分响应>
<错误>
<错误名称>类javax.faces.application.ViewExpiredException< /错误名称>
<错误消息>的ViewID:/viewer.xhtml  - 查看/viewer.xhtml无法恢复< /错误消息>。
< /错误>
< /部分响应>
 

但是,一旦我配置页面是由 j_security_check 保护和执行上面列出,奇怪的是相同的步骤(对我来说)的 ViewExpiredException 不再提高。相反,效应初探只是一个新的视图状态。

  

发表

  javax.faces.ViewState = -4873187770744721574:8069938124611303615
 

     

响应:

 <部分响应>
<修改>
<更新ID =javax.faces.ViewState> 234065619769382809:-4498953143834600826< /更新>
< /修改>
< /部分响应>
 

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

解决方案

我能够重现你的问题。这是怎么回事的是,容器调用 RequestDispatcher的#转发()到登录页面,在安全性约束规定。但是,如果登录页面本身就是一个JSF页面,以及,那么 FacesServlet的将被调用,以及对转发的请求。作为请求是前向,这将简单地在其上创建转发资源(登录页)的新视图。然而,因为它是一个Ajax请求,而且也没有渲染信息(整个POST请求基本上是安全检查前进过程中丢弃),只有视图状态将被退回。

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

这是不幸的是,工作作为设计的。我怀疑是有一些监督在JSF规范,以对Ajax请求的安全性约束检查。原因毕竟是可以理解的,幸运的是容易解决。只是,你其实并不想在这里显示一个错误页面,而是将其全部只需登录页面,完全作为一个非Ajax请求期间会发生。你只需要检查,如果当前请求是一个Ajax请求,并且被转发到登录页面,那么你需要发送一个特殊的重定向Ajax响应,使整个视图将被改变。

您可以做到这一点的,的PhaseListener 如下:

 公共类AjaxLoginListener实现的PhaseListener {

    @覆盖
    公共PhaseId getPhaseId(){
        返回PhaseId.RESTORE_VIEW;
    }

    @覆盖
    公共无效beforePhase(PhaseEvent事件){
        //空操作。
    }

    @覆盖
    公共无效afterPhase(PhaseEvent事件){
        FacesContext的上下文= event.getFacesContext();
        HttpServletRequest的请求=(HttpServletRequest的)context.getExternalContext()调用getRequest()。
        字符串originalURL =(字符串)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
        串loginURL = request.getContextPath()+/login.xhtml;

        如果(context.getPartialViewContext()。isAjaxRequest()
            &功放;&安培; originalURL!= NULL
            &功放;&安培; loginURL.equals(request.getRequestURI()))
        {
            尝试 {
                。context.getExternalContext()重定向(originalURL);
            }赶上(IOException异常E){
                抛出新FacesException(E);
            }
        }
    }
}
 


更新这个解决方案是因为 OmniFaces 1.2 被内置到的 OmniPartialViewContext 。所以,如果你碰巧已经使用OmniFaces,那么这个问题是<一个href="https://github.com/omnifaces/omnifaces/blob/2.1/src/main/java/org/omnifaces/context/OmniPartialViewContext.java#L276"相对=nofollow>完全透明的解决,你并不需要一个定制的的PhaseListener 这一点。

I have a JSF page which is not protected by j_security_check. I perform the following steps:

  1. Open the JSF page in a browser.
  2. Restart the server.
  3. Click a command button on the JSF page to initiate an ajax call.

Firebug shows that a ViewExpiredException is raised, as expected.

Post:

javax.faces.ViewState=8887124636062606698:-1513851009188353364

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>

However, once I configure the page to be protected by j_security_check and perform the same steps listed above, strangely (to me) the ViewExpiredException is no longer raised. Instead, the reponse is just a new view state.

Post:

javax.faces.ViewState=-4873187770744721574:8069938124611303615

Response:

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

Can someone help me figure this out? I expect it to raise an exception so I can process that exception and show an error page. Now it just responds with a new ViewState, my page just got stuck without any visual feedback.

解决方案

I was able to reproduce your problem. What is happening here is that the container invokes a RequestDispatcher#forward() to the login page as specified in security constraint. However, if the login page is by itself a JSF page as well, then the FacesServlet will be invoked as well on the forwarded request. As the request is a forward, this will simply create a new view on the forwarded resource (the login page). However, as it's an ajax request and there's no render information (the whole POST request is basically discarded during the security check forward), only the view state will be returned.

Note that if the login page were not a JSF page (e.g. JSP or plain HTML), then the ajax request would have returned the whole HTML output of the page as ajax response which is unparseable by JSF ajax and interpreted as "empty" response.

It is, unfortunately, working "as designed". I suspect that there's some oversight in the JSF spec as to security constraint checks on ajax requests. The cause is after all understandable and fortunately easy to solve. Only, you actually don't want to show an error page here, but instead just the login page in its entirety, exactly as would happen during a non-ajax request. You just have to check if the current request is an ajax request and is been forwarded to the login page, then you need to send a special "redirect" ajax response so that the whole view will be changed.

You can achieve this with a PhaseListener as follows:

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().redirect(originalURL);
            } catch (IOException e) {
                throw new FacesException(e);
            }
        }
    }
}


Update this solution is since OmniFaces 1.2 been built into the OmniPartialViewContext. So if you happen to use OmniFaces already, then this problem is fully transparently solved and you don't need a custom PhaseListener for this.

这篇关于ViewExpiredException不是扔在Ajax请求,如果JSF页面由j_security_check保护的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆