sessionjsfjsf-2viewexpiredexception

Check if session exists JSF


I have a login page where I have a User bean to authenticate username and password for a person. This Bean is Session Scoped. If someone writes a URL and tries to jump the login page, how can I check that and redirect him to the login page?

On the other hand. Suppose I have logged in and I was working and suddenly I go out for a while and my session expires. When I return and try to interact with the form it sends a message alerting me the session expiration. How can I redirecto again to the login form when this occurs?

Thanks in advance. Hope I explain myself.

Mojarra 2.1.4, Tomcat 7, Tomahawk 1.1.11


Solution

  • If someone writes a URL and tries to jump the login page, how can I check that and redirect him to the login page?

    You seem to using homegrown authentication. In that case, you need to implement a servlet filter. JSF stores session scoped managed beans as attributes of HttpSession, so you could just check on that in doFilter() method:

    HttpServletRequest req = (HttpServletRequest) request;
    UserManager userManager = (UserManager) req.getSession().getAttribute("userManager");
    
    if (userManager != null && userManager.isLoggedIn()) {
        chain.doFilter(request, response);
    } else {
        HttpServletResponse res = (HttpServletResponse) response;
        res.sendRedirect(req.getContextPath() + "/login.xhtml");
    }
    

    Map this filter on an URL pattern covering the secured pages, e.g. /app/*.


    When I return and try to interact with the form it sends a message alerting me the session expiration. How can I redirect again to the login form when this occurs?

    I understand that this concerns Ajax requests? For normal requests you could have used an <error-page> in web.xml. If setting the state saving method to client in web.xml as follows

    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>
    

    is not an option, then you need to implement a custom ExceptionHandler:

    public class ViewExpiredExceptionHandler extends ExceptionHandlerWrapper {
    
        private ExceptionHandler wrapped;
    
        public ViewExpiredExceptionHandler(ExceptionHandler wrapped) {
            this.wrapped = wrapped;
        }
    
        @Override
        public void handle() throws FacesException {
            FacesContext facesContext = FacesContext.getCurrentInstance();
    
            for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator(); iter.hasNext();) {
                Throwable exception = iter.next().getContext().getException();
    
                if (exception instanceof ViewExpiredException) {
                    facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, "viewexpired");
                    facesContext.renderResponse();
                    iter.remove();
                }
            }
    
            getWrapped().handle();
        }
    
        @Override
        public ExceptionHandler getWrapped() {
            return wrapped;
        }
    
    }
    

    (note that this particular example navigates to viewexpired, so it expects a /viewexpired.xhtml as error page)

    The above needs to be baked by the following ExceptionHandlerFactory implementation:

    public class ViewExpiredExceptionHandlerFactory extends ExceptionHandlerFactory {
    
        private ExceptionHandlerFactory parent;
    
        public ViewExpiredExceptionHandlerFactory(ExceptionHandlerFactory parent) {
            this.parent = parent;
        }
    
        @Override
        public ExceptionHandler getExceptionHandler() {
            return new ViewExpiredExceptionHandler(parent.getExceptionHandler());
        }
    
    }
    

    which in turn needs to be registered in faces-config.xml as follows:

    <factory>
        <exception-handler-factory>com.example.ViewExpiredExceptionHandlerFactory</exception-handler-factory>
    </factory>