Failing to redirect from JSF phaselistener

2020-03-31 09:10发布

My problem is similar to the one here but doesn't involve PrimeFaces in any way. Also, I couldn't find a real solution there.

I'm basically trying to get the phaselistener to redirect to login page in case there is no JSF session (When, for example, session has timed out)

I'm trying to redirect from within a JSF 2 Phaselistener. To sum up, what I'm doing is this:

public void beforePhase(PhaseEvent event) {
    PhaseId id = event.getPhaseId();
    if(id.equals(PhaseId.RESTORE_VIEW)){
        FacesContext context = event.getFacesContext();
        Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
        if(sessionMap==null || sessionMap.isEmpty()){
            // No Session, Redirect to login
            try {
                context.getExternalContext().redirect("/login");
            } catch (Exception e) {
                ...
            }
        }
    }
}

When the redirect code runs I get this exception:

java.lang.NullPointerException
        at org.apache.myfaces.context.servlet.PartialViewContextImpl.getPartialResponseWriter(PartialViewContextImpl.java:301)
        at org.apache.myfaces.context.servlet.ServletExternalContextImpl.redirect(ServletExternalContextImpl.java:452)
        at com.AuthenticationPhaseListener.userIsNotLogedIn

What could be causing this? Am I doing this wrong?

Thanks!

2条回答
贼婆χ
2楼-- · 2020-03-31 09:53

Well I had the same problem like you but I did not solve in in such a complicated way as you are doing.My steps

1) create a class that implements the PhaseListener
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 *
 * @author fakumawah @date 09.03.2012
 */
public class LoggedInCheck extends BackingBean implements PhaseListener
{

@Override`enter code here`
 public PhaseId getPhaseId()
  {
    return PhaseId.ANY_PHASE;
  }

  @Override
  public void beforePhase(PhaseEvent event)
  {
  }

  @Override
  public void afterPhase(PhaseEvent event)
  {
    FacesContext fc = event.getFacesContext();
    boolean loginPage = fc.getViewRoot().getViewId().lastIndexOf("login") > -1 ? true :     false;
    if (!loginPage && !loggedIn())
    {
      NavigationHandler nh = fc.getApplication().getNavigationHandler();
      nh.handleNavigation(fc, null, "gotologin");
    }
  }

  private boolean loggedIn()
  {
    return getSessionWrapper().isLoggedInAgain();
  }
}

Sorry for the uncommented code but I guess the code is really easy to understand. Most important thing to note is the afterPhase(..) which checks if I am in the logged in or if I have a session already. If I don´t it creates a navigator and navigates me to the login page

2) Your isLoggedInAgain() should look something like this:

/**
   * A method to check if Username is already logged in
   * @param username
   * @return 
   */
  public boolean isLoggedInAgain()
  {
    if (session != null) // Check if session is not null
    {
      if (session.isConnected()) // Check if session is connected   
      {
        return true;
        //destroyIfsLibrarySession();  // Session is available -> destroy session
      }
      else  // session is not available
      {
        logger.log(Level.SEVERE, "Already logged out");
        return false;
      }
    }
    return false;
  }

Since I am dealing with LibrarySessions from Oracle CMSDK that is why my test for the session looks like this. Most important is to check your session somehow and give out true or false, depending on if session exist or not.

3) Configure the Listener in faces-config.xml

<!-- Phase-Listener to check if user is logged in or not -->
<lifecycle>
    <phase-listener>com.mycompany.mypackagename.webapp.LoggedInCheck</phase-listener>
</lifecycle>

4) Lastly define a navigation rule for the "gotologin"

<!-- Navigation Rule for sending user to login page from an expired session -->
    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>gotologin</from-outcome>
            <to-view-id>/login.em</to-view-id>
            <redirect />
        </navigation-case>
    </navigation-rule>

And that is it, whenever you do not have a session on any page and are not on the login page, you will be carried to the login page.

Enjoy

查看更多
闹够了就滚
3楼-- · 2020-03-31 09:59

This seem to be happening during an ajax request. I'm not sure about the exact cause, the stacktrace at least indicates a possible bug in MyFaces implementation.

At least, the overall design approach is poor. This kind of HTTP request/response modifications should preferably not take place in a PhaseListener. There it is not intended for. You want to do this kind of job in a normal servlet Filter instead.

查看更多
登录 后发表回答