如果 JSF 页面受 j_security_check 保护,则不会在 ajax 请求上抛出 ViewExpiredException [英] ViewExpiredException not thrown on ajax request if JSF page is protected by j_security_check

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

问题描述

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().invalidateSession();
                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.

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

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