Dealing with ViewExpiredException on Regular and Ajax Requests

This is a note to myself mainly ( this is based on Oleg’s post btw, I just tried to simplified a little from here

To deal with ViewExpiredException with regular and ajax requests, do the following:

in faces-config.xml, have the following:


    <factory>
        <exception-handler-factory>
            com.prime.DefaultExceptionHandlerFactory
        </exception-handler-factory>
    </factory>
	
      <lifecycle>
	 <phase-listener>
            com.prime.SecurityPhaseListener
        </phase-listener>
	</lifecycle>

in security.xml for Spring, we add a entry-point-ref as follows:

<http auto-config="false" access-denied-page="/accessDenied.xhtml"
		entry-point-ref="authenticationProcessingFilterEntryPoint">

where authenticationProcessingFilterEntryPoint is :

<beans:bean id="authenticationProcessingFilterEntryPoint"
		class="com.prime.AuthenticationProcessingFilterEntryPoint">
		<beans:property name="loginFormUrl" value="/login.xhtml" />
		<beans:property name="useForward" value="true" />
	</beans:bean>

ok, now our java codes;

the SecurityPhaseListener is defined as:

package com.prime;

import java.io.IOException;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.primefaces.context.DefaultRequestContext;

public class SecurityPhaseListener implements PhaseListener {

	private static final long serialVersionUID = -101374487855763803L;

	public void afterPhase(PhaseEvent event) {
	}

	public void beforePhase(PhaseEvent event) {
		FacesContext fc = event.getFacesContext();
		String loginPage = (String) fc.getExternalContext().getRequestMap().get(AuthenticationProcessingFilterEntryPoint.ATTRIBUTE_LOGIN_PAGE);
		if (StringUtils.isNotBlank(loginPage)) {
			doRedirect(fc, loginPage);
		}
	}

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

	public void doRedirect(FacesContext fc, String redirectPage) throws FacesException {
		ExternalContext ec = fc.getExternalContext();

		try {
			if (ec.isResponseCommitted()) {
				return;
			}

			ec.redirect(ec.getRequestContextPath() + (redirectPage != null ? redirectPage : ""));

		} catch (IOException e) {
			throw new FacesException(e);
		}
	}
}

our AuthenticationProcessingFilterEntryPoint code is as follows:

package com.prime;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

/**
 * Extended Spring AuthenticationProcessingFilterEntryPoint for playing together with JSF Ajax redirects.
 */
public class AuthenticationProcessingFilterEntryPoint extends LoginUrlAuthenticationEntryPoint
{
    public static final String ATTRIBUTE_LOGIN_PAGE = "com.myproject.login.page";


      @Override
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();
    }
    
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

    	if(request.getParameter("javax.faces.partial.ajax") != null )
    		request.setAttribute(ATTRIBUTE_LOGIN_PAGE, getLoginFormUrl());
        super.commence(request, response, authException);
    }
}

ok finally, the DefaultExceptionHandlerFactory classes which are as follows:

package com.prime;

import java.util.Iterator;

import javax.faces.FacesException;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.servlet.http.HttpSession;

public class DefaultExceptionHandler extends ExceptionHandlerWrapper {

    private ExceptionHandler wrapped;

    public DefaultExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return this.wrapped;
    }

    @Override
    public void handle() throws FacesException {

        FacesContext facesContext = FacesContext.getCurrentInstance();
        Iterator<ExceptionQueuedEvent> eventIterator = getUnhandledExceptionQueuedEvents().iterator();

        ViewExpiredException viewExpiredException = getViewExpiredException(getUnhandledExceptionQueuedEvents());
        if (viewExpiredException != null) {
            String redirectUrl = invalidateSessionAndGetErrorPage(facesContext, viewExpiredException);
            redirectToTheErrorPage(facesContext, redirectUrl);
        } else if (eventIterator.hasNext()) {

            String redirectUrl = "/error.html";

            redirectToTheErrorPage(facesContext, redirectUrl);
        }
    }


    private String invalidateSessionAndGetErrorPage(FacesContext facesContext, ViewExpiredException throwable) {

        HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(false);
        if (session != null) {
            session.invalidate();
        }

        return "/dashboard.xhtml";
    }

    private void redirectToTheErrorPage(FacesContext facesContext, String redirectUrl) {
        SecurityPhaseListener spl = new SecurityPhaseListener();
        spl.doRedirect(facesContext, redirectUrl);

    }

    private ViewExpiredException getViewExpiredException(Iterable<ExceptionQueuedEvent> unhandledExceptionQueuedEvents) {
        for (ExceptionQueuedEvent event : unhandledExceptionQueuedEvents) {
            ExceptionQueuedEventContext queuedEventContext = event.getContext();
            if (queuedEventContext.getException() instanceof ViewExpiredException) {
                return (ViewExpiredException) queuedEventContext.getException();
            }
        }
        return null;
    }

}

and our DefaultExceptionHandlerFactory is

package com.prime;

import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;

public class DefaultExceptionHandlerFactory extends ExceptionHandlerFactory {
	private ExceptionHandlerFactory parent;

	public DefaultExceptionHandlerFactory(ExceptionHandlerFactory parent) {
		this.parent = parent;
	}

	@Override
	public ExceptionHandler getExceptionHandler() {
		ExceptionHandler eh = parent.getExceptionHandler();
		eh = new DefaultExceptionHandler(eh);

		return eh;
	}
}

the codes in DefaultExceptionHandlerFactory can be changed to customize.

just a note myself.