JSF/Primefaces and session timeout login redirection


If you use JSF and Primefaces you can encouter the case where your session expired and the next action your user does is an Ajax request. If you configured a form login then the ajax request will get the form redirection as expected but…it is an ajax call so your user will see nothing basically excepted an irresponsive GUI.

To work around it you can implement a custom PhaseListener checking if the request is an ajax request redirected to the login page and if so enforce the redirection through JSF API.

A quick reminder before getting into JSF: to get a FORM login in a webapp you can use something like:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="
           http://java.sun.com/xml/ns/javaee
           http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" >

    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/security/login.xhtml</form-login-page>
            <form-error-page>/security/login.xhtml</form-error-page>
        </form-login-config>
    </login-config>

    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

</web-app>

Now to detect if JSF is doing a redirection during an AJAX call you can use:

package com.github.rmannibucau.jsf;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class SessionTimeoutListener implements PhaseListener {
    private String getLoginPath() {
        return "/security/login.xhtml";
    }

    @Override
    public void beforePhase(final PhaseEvent event) {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        if (!facesContext.getPartialViewContext().isAjaxRequest() || facesContext.getRenderResponse()) { // not ajax or too late
            return;
        }

        final HttpServletRequest request = HttpServletRequest.class.cast(facesContext.getExternalContext().getRequest());
        if (request.getDispatcherType() == DispatcherType.FORWARD && getLoginPath().equals(request.getServletPath())) { // isLoginRedirection()
            final String redirect = facesContext.getExternalContext().getRequestContextPath() + request.getServletPath();
            try {
                facesContext.getExternalContext().redirect(redirect);
            } catch (final IOException e) {
                // here use you preferred logging framework to log this error
            }
        }
    }

    @Override
    public void afterPhase(final PhaseEvent event) {
        // no-op
    }

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

Of course cause Servlet API doesn’t provide a way to get LoginConfig from code you need to ensure getLoginPath() matches your web.xml configuration.

TIP: if you want from the ServletContext you can get the stream of /WEB-INF/web.xml and reparse in a light fashion the web.xml to get this value. Just ensure to do it only once for performances reason ;).

Finally activate this PhaseListener in your WEB-INF/faces-config.xml:

<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
              version="2.2">

    	<lifecycle>
        <phase-listener>com.github.rmannibucau.jsf.SessionTimeoutListener</phase-listener>
    </lifecycle>

</faces-config>

From now on, each time your session will expire and the container will redirect an AJAX session, your user will follow this redirection too.

Tested with Mojarra 2.2, TomEE 1.7.3 and Primefaces 5.3 🙂

Want more content ? Keep up-to-date on my new blog

Or stay in touch on twitter @rmannibucau

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s