2

I have implemented a session listener. i want that when a user tries to use the site after his session is destroyed, he should be redirected to the welcome page (login page). I have tried this by doing loging?faces-redirect=true but i have to click twice before i am really redirected to the loginpage. Further, when session expires when on the welcome page (login page). the application crashes as shown in the following errors:

    WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
javax.faces.application.ViewExpiredException: viewId:/loginpage.xhtml - View /loginpage.xhtml could not be restored.
        at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:205)
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
        at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
        at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:725)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1019)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:225)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
        at java.lang.Thread.run(Thread.java:662)

i uses primefaces 3.0, glassfish 3.1.1 on netbeans Thanks.

maress
  • 3,533
  • 1
  • 19
  • 37

2 Answers2

1

You can either use a servlet filter or JSF phase listener to redirect to login page if the user is not authenticated.

Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
  • JSF phase listener is not a very ideal solution in this case. Servlet filter should be used. – Apurv Jan 15 '12 at 17:58
0

We had similar issues in our application. Below is the solution we ended up using. We use a phase listener to redirect to the login page in the event of an expired session (and they are not on the login page). We then use a custom view handler to prevent the user from experiencing an expired session on the login page. Basically if we see the session has expired on the login page we make a new one.

Note: Parts of the code that need updated for a specific use case are marked. We came up with this approach by putting together several examples for specific issues we found online. Some of the references are:

http://www.gregbugaj.com/?p=164

https://stackoverflow.com/a/6816513/2212458

https://stackoverflow.com/a/4992869/2212458

Here is the phase listener responsible for making sure that a visitor has a session and forwarding them to the login page if they don't have one (such as when it has expired). It also performs 2 other checks. It makes sure if they have a session, that they are authenticated (logged in), and it makes sure that they have access rights to the page they are hitting.

import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.*;
import javax.servlet.http.HttpSession;

/**
 * A phase listener.  Runs after the restore view phase.  Makes sure that the user is logged on
 * to view any page other than the login page.
 */
public class AuthorizationListener implements PhaseListener
{
    /**
     * Called after phase executes.  Makes sure we are logged in if we are not on the login page.
     * @param phaseEvent
     */
    @Override
    public void afterPhase(PhaseEvent phaseEvent)
    {
        // get page we are on
        FacesContext facesContext = phaseEvent.getFacesContext();
        String currentPage = facesContext.getViewRoot().getViewId();

        // determine if we are on the login page
        boolean isLoginPage = currentPage.contains("login"); <--- CHANGE
        if (isLoginPage)
        {
            return;
        }

        // get session - do not create one if it does not exist
        HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(false);

        // no session is present
        if(session==null)
        {
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, "login?faces-redirect=true&reason=expired"); <--- CHANGE
            return;
        }

        // if not logged in send to login page
        if (USER IS NOT LOGGED IN) <--- CHANGE
        {
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, "login?faces-redirect=true&reason=expired"); <--- CHANGE
            return;
        }

        // they are logged in, make sure they have rights to page they are visiting
        if (USE DOES NOT HAVE RIGHTS TO THE PAGE THEY ARE VISITING) <--- CHANGE
        {
            // user does not have privilege to go to this page
            NavigationHandler nh = facesContext.getApplication().getNavigationHandler();
            nh.handleNavigation(facesContext, null, accessDenied); <--- CHANGE
        }
    }

    /**
     * Called before phase executes.  Does nothing.
     * @param phaseEvent the phase event
     */
    @Override
    public void beforePhase(PhaseEvent phaseEvent)
    {
        // intentionally left blank
    }

    /**
     * Identifies the phase we want to listen and respond to.
     * @return the phase
     */
    @Override
    public PhaseId getPhaseId()
    {
        return PhaseId.RESTORE_VIEW;
    }
}

Here is the custom view handler responsible for stopping session expiration on the login page.

import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import java.io.IOException;

/**
 * This class adds additional behavior to the facelet view handler.  Specifically it
 * prevents the user from experiencing session/view timeout errors at the login screen.
 */
public class CustomViewHandler extends ViewHandlerWrapper
{
    /** The default view handler we are adding extra behavior to. */
    private ViewHandler wrapped;

    /**
     * Constructor.
     * @param wrapped the wrapped handler.  Ref.
     */
    public CustomViewHandler(ViewHandler wrapped)
    {
        super();
        this.wrapped = wrapped;
    }

    /**
     * Expose the wrapped handler (required by base class).
     * @return the handler.  Ref.
     */
    @Override
    public ViewHandler getWrapped()
    {
        return wrapped;
    }

    /**
     * Called when a view is restored.  Prevents expiration on login page.
     * @param facesContext the context for this request
     * @param viewId the view identifier for the current request
     * @return the restored view
     */
    @Override
    public UIViewRoot restoreView(FacesContext facesContext, String viewId)
    {
        // have the wrapped handler restore the view
        UIViewRoot root = super.restoreView(facesContext, viewId);

        // if there was no view to restore (maybe because it expired)
        if (root == null)
        {
            // if the view expired on the login page make a new view, don't allow login page to expire
            if ( viewId.contains("login") ) <--- CHANGE
            {
                // create a new view
                // for some reason the starting slash is required else we get errors in server log about not finding the page
                root = createView(facesContext, "/" + "login"); <--- CHANGE
                // saves view - without this session never gets created so we will just keep hitting this code
                facesContext.renderResponse();
            }
        }

        return root;
    }

    /**
     * Called when a view is rendered.  Does nothing but log a message.
     * @param context the context for this request
     * @param viewToRender the view to render
     * @throws IOException thrown if an input/output error occurs in wrapped handler
     */
    @Override
    public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException
    {
        super.renderView(context, viewToRender);
    }
}

Changes to the config files are required to use this code.

Additions to faces-config.xml

<view-handler>PACKAGE.CustomViewHandler</view-handler> <--- CHANGE
<lifecycle>
    <phase-listener>PACKAGE.AuthorizationListener</phase-listener> <--- CHANGE
</lifecycle>

Additions to web.xml

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/login.xhtml?reason=expired</location> <--- CHANGE
</error-page>-<session-config><session-timeout> 10 </session-timeout></session-config>
<listener><listener-class> com.sun.faces.config.ConfigureListener </listener-class></listener>
Community
  • 1
  • 1
denver
  • 2,863
  • 2
  • 31
  • 45