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.