1

I want to redirect a user to a start view when he clicks the logout button. The logout is done correctly, and after the start view is requested the user is presented a login page. So far so good, its what i want because the start view is protected and i intercept with a phase listener which redirects to the login page.

Logout action:

public String logout() {

    FacesContext context = FacesContext.getCurrentInstance();
    ExternalContext externalContext = context.getExternalContext();

    // invalidate session
    Object session = externalContext.getSession(false);
    HttpSession httpSession = (HttpSession) session;
    httpSession.invalidate();

    // let the navigation rule decide where to go...
    return null;
}

Faces navigation rule:

<navigation-rule>
    <navigation-case>
        <from-action>#{loginBean.logout}</from-action>
        <to-view-id>/faces/index.xhtml</to-view-id>
        <redirect />
    </navigation-case>
</navigation-rule>

ForceLoginListener:

@Override
public void beforePhase(PhaseEvent event) {

    FacesContext context = FacesContext.getCurrentInstance();

    // extract view id from original request
    String viewId = context.getViewRoot().getViewId();

    if (// viewId is an 'protected' view AND not logged in //) {

        Application app = context.getApplication();

        // redirect to the login view
        ViewHandler viewHandler = app.getViewHandler();
        UIViewRoot viewRoot = viewHandler.createView(context, Constants.LOGIN_VIEW);
        context.setViewRoot(viewRoot);

    }

}

@Override
public void afterPhase(PhaseEvent event) {
}

@Override
public PhaseId getPhaseId() {
    return PhaseId.RENDER_RESPONSE;
}

The problem i have now is that the URL which is visible to the user is not the start view nor the login view, it is the view which was active just the moment before the user pressed the logout button.

I thought the redirect in the navigation rule should do the trick as it does for all my other navigation cases, but in this case it does not work. Does anybody have an idea why?

Thanks in advance.

Joysn
  • 987
  • 1
  • 16
  • 30
  • I don't get why you should add the PhaseListener. Have you tried using your logout without it? It makes any difference? – Luiggi Mendoza Jul 22 '12 at 15:42
  • The PhaseListener is there to intercept requests to 'protected' pages. Not for the logout. The request flow is like this: some pageX is displayed, logout button, redirect to index via navigation rule, phase listener intercepts and sets the login view. Here the flow stops. I assumed that the browser shows index in its location field, but there is still pageX written. – Joysn Jul 22 '12 at 20:42

2 Answers2

1

Your strange ForceLoginListener approach needs to be replaced by a normal servlet filter.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • well, i took this listener from the book "Beginning JSF 2 APIs and JBoss Seam", so i thought it is a valid approach. Now i changed to a filter which redirects me to the login page. – Joysn Jul 29 '12 at 14:43
  • but what about using form based login with JSF and container managed security? i added a security-domain and a login-module to JBoss and thought i can add the cfg to `web.xml` and implement a JSF form with j_security_check and so on. but when the container is handling the authentication, how can i get back the user and role/group information which i need in the application? i implemented an Authenticator EBJ and an UserSession bean which handles all of the needed stuff ... – Joysn Jul 29 '12 at 14:50
  • and how can i add certificate based logins with that approach? if i understood correct certificate based logins are supported by containers, but how can i get the needed information for the application? – Joysn Jul 29 '12 at 15:03
  • See http://stackoverflow.com/questions/2206911/best-way-for-user-authentication-on-javaee-6-using-jsf-2-0/2207147#2207147 for several ways. – BalusC Jul 29 '12 at 16:19
  • finally i removed the login filter and the 'force login listener' and use FORM based authentication using an login bean which uses request.login() (and request.logout() respectively) with container managed security. navigation rules help to get back to the login view after logout and session invalidation. furthermore i 'remember' the page which was requested when the login page is displyed so i can redirect back to that original requested page. with the query string attached i have bookmarkable urls now too. many thanks for your hints :) – Joysn Jul 29 '12 at 21:32
0

I don't really understand what is happening exactly in your case, but you could just change your logout action to redirect using the externalContext, and eliminate all the fuss with navigation rules:

public void logout() throws IOException {
    FacesContext context = FacesContext.getCurrentInstance();
    ExternalContext externalContext = context.getExternalContext();

    // invalidate session
    Object session = externalContext.getSession(false);
    HttpSession httpSession = (HttpSession) session;
    httpSession.invalidate();

    // redirect to your login view:
    externalContext.redirect(Constants.LOGIN_VIEW);
}
Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
  • it seems the redirect does not go through the JSF rendering, as the xhtml page is directly sent to the browser. And i needed to add externalContext.getRequestContextPath() so that the page was found. – Joysn Jul 22 '12 at 16:26
  • hmm... if the xhtml content is being sent to the browser, you may have some misconfiguration on the mapping in `web.xml`. how are the mappings for your faces servlet on `web.xml`? – Elias Dorneles Jul 22 '12 at 16:43
  • i think the behavior is how it should be. As the Constants.LOGIN_VIEW only contains "/faces/login.xhtml" because i usually use it with UIViewRoot and the context to set the viewRoot, which works nicely. Sure, i can add another URI which ends in .jsf which i mapped in web.xml to the Faces servlet. But the question is, why the redirect in the navigation cases works for everything except when i invalidated the session and want to redirect to the login page, which actually works. Only the old URL is displayed in the browsers location field. Thats only a cosmetic issue, but anyway interesting :) – Joysn Jul 22 '12 at 20:37
  • Well, I don't really know all the implications of resetting the view root... But if you're resetting it exactly after the action method was called, before the render response phase, could it be that the navigation rule looked up in render response phase be one registered for the login view instead of for the logout (that is what you are expecting)? – Elias Dorneles Jul 22 '12 at 20:50
  • The servlet mapping: ` Faces Servlet *.jsf ` – Joysn Jul 22 '12 at 20:50
  • yeah, with that mapping you would have to use `"/faces/login.jsf"` instead of `"/faces/login.xhtml"`, or just add another mapping for `*.xhtml`. – Elias Dorneles Jul 22 '12 at 20:51