-3

ViewExpiredException not thrown on ajax request if JSF page is protected by j_security_check provides the solution when the login page is the JSF page. But what if the login page is a JSP page?

I have a jsp login page:

<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/core/login.jsp</form-login-page>
        <form-error-page>/core/login.jsp</form-error-page>
    </form-login-config>
</login-config>

In my JSF page (facelet), if I click on non-Ajax button, I will be redirected to the login.jsp. If I click an Ajax button, it will remain on the same JSF page. However, in both cases the debugging code that I added to the JSP login page will show up in a console.

===

I re-wrote the login page using a facelet instead of a jsp

<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/facelets/login.jsf</form-login-page>
        <form-error-page>/facelets/login.jsf</form-error-page>
    </form-login-config>
</login-config>

I added an AjaxLoginListener:

public class AjaxLoginListener implements PhaseListener {

        @Override
        public PhaseId getPhaseId() {
    //        return PhaseId.ANY_PHASE;
            return PhaseId.RESTORE_VIEW;
        }

        @Override
        public void beforePhase(PhaseEvent event) {
            System.out.println("    **** AjaxLoginListener: Before Phase: " + event.getPhaseId());
            // NOOP.
        }

        @Override
        public void afterPhase(PhaseEvent event) {
            System.out.println("    **** AjaxLoginListener: After Phase: " + event.getPhaseId());
            FacesContext context = event.getFacesContext();
            HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
            String originalURL = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
            String loginURL = request.getContextPath() + "/facelets/login.jsf";
            System.out.println("    **** "+new java.sql.Timestamp(System.currentTimeMillis()) + " -- "
                    + this.getClass().getName()+" AjaxLoginListener: After Phase: originalURL: " + originalURL + " Login URL: "+loginURL);
            System.out.println("    **** AjaxLoginListener: After Phase: request.getRequestURI() " +request.getRequestURI());

            System.out.println("    **** "+new java.sql.Timestamp(System.currentTimeMillis()) + " -- "
                    + this.getClass().getName()+" AjaxLoginListener: After Phase: isAjaxRequest " +context.getPartialViewContext().isAjaxRequest());

            if (context.getPartialViewContext().isAjaxRequest()
                    && originalURL != null
                    && loginURL.equals(request.getRequestURI()))
            {
                System.out.println("    **** "+new java.sql.Timestamp(System.currentTimeMillis()) + " -- "
                        + this.getClass().getName()+" AjaxLoginListener: After Phase: AjaxRequest " + event.getPhaseId());
                try {
                    context.getExternalContext().redirect(originalURL);
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new FacesException(e);
                }
            }
        }
    }

Once I used a facelet instead of a jsp I was able to invoke the listener. However, it never goes to the login page because the originalURL (RequestDispatcher.FORWARD_REQUEST_URI) is always NULL for Ajax requests. What am doing wrong?

Community
  • 1
  • 1
  • Just edit `loginURL` accordingly? What happens when you try that? I'm not sure if I understand your concrete problem. Note that I assume that you're absolutely positive that you've *exactly* the same problem as the OP. – BalusC Dec 18 '12 at 18:06
  • I believe the problem is as you describe it: "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." I just can't figure out how to re-direct to login page in this case. – discoverer66 Dec 18 '12 at 18:43
  • In this case, `loginURL` is just `request.getContextPath() + "/core/login.jsp"`. Have you tried it? The described problem is just what's incorrectly been returned as ajax response. – BalusC Dec 18 '12 at 18:49
  • Yes, I am using request.getContextPath() + "/core/login.jsp" as loginURL. The strange thing that I noticed: when session expires (I set to 10 sec: #{session.setMaxInactiveInterval(10)}) and I click on an Ajax button, it never hits the AjaxLoginListener but the debugging code from login.jsp is shown in the console. And when I click on a non-Ajax button it hits the AjaxLoginListener first. – discoverer66 Dec 18 '12 at 18:55
  • You need to make it a JSF request. You need to change the URL to match faces servlet's URL pattern. E.g. if it's `*.jsf`, you need `/core/login.jsf` and `request.getContextPath() + "/core/login.jsf"` respectively. Otherwise, add `/core/*` to the faces servlet mapping. – BalusC Dec 18 '12 at 18:57
  • Shouldn't in this case the login page be converted from JSP to facelet? – discoverer66 Dec 18 '12 at 20:07
  • Not necessary, I would however recommend it. Note that you can perfectly fine put a plain vanilla HTML form in a JSP or Facelets file. – BalusC Dec 18 '12 at 20:10
  • Converted login jsp to a facelet but get null for RequestDispatcher.FORWARD_REQUEST_URI all the time for all Ajax requests. – discoverer66 Dec 20 '12 at 17:33

1 Answers1

0

The simple solution is to add a check if the user is logged in and if not, to redirect to the login page:

    @Override
    public void afterPhase(PhaseEvent event) {
        System.out.println("    **** AjaxLoginListener: After Phase: " + event.getPhaseId());
        FacesContext context = event.getFacesContext();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
        HttpSession session = request.getSession();

//        String originalURL = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);

        String loginURL = request.getContextPath() + "/facelets/login.dti";
        System.out.println("    **** "+new java.sql.Timestamp(System.currentTimeMillis()) + " -- "
                + this.getClass().getName()+" AjaxLoginListener: After Phase:  Login URL: "+loginURL);
        System.out.println("    **** AjaxLoginListener: After Phase: request.getRequestURI() " +request.getRequestURI());

        System.out.println("    **** "+new java.sql.Timestamp(System.currentTimeMillis()) + " -- "
                + this.getClass().getName()+" AjaxLoginListener: After Phase: isAjaxRequest " +context.getPartialViewContext().isAjaxRequest());

        if (context.getPartialViewContext().isAjaxRequest()
                && session.getAttribute("user") == null
                && loginURL.equals(request.getRequestURI()))
        {
            System.out.println("    **** "+new java.sql.Timestamp(System.currentTimeMillis()) + " -- "
                    + this.getClass().getName()+" AjaxLoginListener: After Phase: AjaxRequest " + event.getPhaseId());
            try {
                context.getExternalContext().redirect(loginURL);
            } catch (IOException e) {
                e.printStackTrace();
                throw new FacesException(e);
            }
        }

    }

However, the significant drawback here, that it would not go to the original page after login.