4 Replies Latest reply: Feb 24, 2012 4:30 AM by Anthony O. RSS

    How does Seam 3 handles the “redirect to capture view” feature after login?

    Anthony O. Newbie

      Hi,

       

      I've just posted my question in stackoverflow, but perhaps there is a more active community here around Seam...

       

      Here is my use cases.

      I have a login page which is /public/login.xhtml. All my other pages are required to log-in before reaching them. They are in /pages/ directory.

      I want that :

      1. If my user access to http://host/myapp/pages/* it redirects him first to the login page, and then, to the URL he has firstly entered.
      2. If my user access to http://host/myapp/, it redirects him first to the login page, and then, to/pages/home.xhtml.
      3. If my user access to http://host/myapp/public/login.xhtml, it redirects him first to the login page, and then, to /pages/home.xhtml.
      4. If my user access to http://host/myapp/public/login.xhtml and is already logged in, it redirects to /pages/home.xhtml.

      What is working currently?

      With Seam 3 (v3.1.0.Final) and the Security + Faces module, my use case n°1 is automagically working with :

      @ViewConfig
      public interface PagesConfig {
         
      static enum Pages {
             
      @ViewPattern("/pages/*")
             
      @LoginView("/public/login.xhtml")
             
      @LoggedIn
              LOGGED_IN_PAGES
      ,
         
      }
      }

      My problem is that I don't understand how Seam's working to do that redirection to the "capture view".

      With Seam 2, it was easy to understand, in components.xml we had

      <event type="org.jboss.seam.security.notLoggedIn">
         
      <action execute="#{redirect.captureCurrentView}" />
      </event>
      <event type="org.jboss.seam.security.loginSuccessful">
         
      <action execute="#{redirect.returnToCapturedView}" />
      </event>

      So we captured the events notLoggedIn and loginSuccessful to handle that with a redirectcomponent.

      In Seam 3, I didn't found that configuration : nothing seems to @Observes LoggedInEvent, and there is no Redirect class...

      The point n°2 is achieved with that /index.htm file :

      <html><head>
         
      <meta http-equiv="Refresh" content="0; URL=pages/home.xhtml">
      </head></html>

      But for my point n°3, I've tried solutions which don't fully work.

      First I tried that in login.xhtml :

      <f:metadata>
         
      <s:viewAction action="#{loginAction.redirectToHome}" if="#{identity.loggedIn}" immediate="true" />
      </f:metadata>

      And with or without onPostback="true", after I login, I'm still in the login page with that error message (twice) : "Unable to find matching navigation case with from-view-id «/public/login.xhtml» for action «#{identity.login}» with outcome «success».". It's only if I now re-access tohttp://host/myapp/public/login.xhtml that my viewAction redirects me to the home.

      I also tried that navigation-rule in faces-config.xml :

      <navigation-rule>
         
      <from-view-id>/public/login.xhtml</from-view-id>

         
      <navigation-case>
             
      <if>#{identity.loggedIn}</if>
             
      <to-view-id>/pages/home.xhtml</to-view-id>
             
      <redirect />
         
      </navigation-case>
      </navigation-rule>

      But then, my use case n°1 was disabled : every time I logged-in, I was redirected to the home.

      Finally, for my point n°4, the s:viewAction does the job.


      So does somebody knows the best practices in order to correctly handle those 4 use cases (which I think are common use cases), especially the point n°3?

        • 1. Re: How does Seam 3 handles the “redirect to capture view” feature after login?
          Jason Porter Master

          Hm, I'm not really sure what's going on here. We'd have to dig in a bit.

          • 2. Re: How does Seam 3 handles the “redirect to capture view” feature after login?
            Brian Leathem Master

            Use case No. - 1 SeamFaces stores the originally requested viewId in the user Session, then re-routes to that view after the successful login. It does this by intercepting the navigation from the Seam Security login button, and fires a PostLoginEvent with the data stored in the SessionMap.

            Use case No. 2 - nice solution with the redirect! You could also do this with a @UrlMapping in your ViewConfig.

            Use case No. 3 - Your viewAction solution should work, but I believe you are coming across SEAMFACES-179. There are a couple of solutions you can use:

            1) In your login method, you can manipulate the seesion map stored by the Seam Faces, as demonstrated in this gist -- (this solution courtesy of Cody Lerum)

            2) Use PrettyFaces to intercept the request for the login view, and rediret you if you are not logged in.

            • 3. Re: How does Seam 3 handles the “redirect to capture view” feature after login?
              Anthony O. Newbie

              Thank you for your reply, I'm trying to implement your 1) solution, but to do that, I must disable theLoginListener of Seam to use mine... and I don't manage to do it Here is what I'm facing now :

              http://stackoverflow.com/questions/9332311/how-to-use-alternative-extending-a-concrete-class-in-a-webapp.

               

              By the way, I've added a comment to SEAMFACES-179.

              • 4. Re: How does Seam 3 handles the “redirect to capture view” feature after login?
                Anthony O. Newbie

                (this is a copy of my reply on stackoverflow)

                Finally here is what I did.

                <f:metadata>
                   
                <s:viewAction action="#{loginAction.redirectToHome}" immediate="true" />
                </f:metadata>

                So I removed the if="#{identity.loggedIn}" in order to call my redirectToHome method which redirects to the /pages/home.xhtml.

                • If the user is already authenticated, then he is redirected to the home page.
                • If he's not, then it is redirected to the home page, which redirects him to the login page thanks to my @ViewConfig

                Here is the loginAction :

                public void redirectToHome() throws IOException {
                    externalContext
                .redirect(externalContext.encodeActionURL(externalContext.getRequestContextPath()+"/pages/home.xhtml"));
                }

                The problem I faced then was when I logged out.

                Here is my logout action :

                <h:commandLink  action="/public/login" actionListener="#{securityAction.logout()}" value="Disconnect" immediate="true" />

                And the securityAction.logout() method :

                public void logout() {
                    identity
                .logout();
                   
                if (!conversation.isTransient()) {
                        conversation
                .end();
                   
                }
                }

                The problem is that I was redirected to the login page (thanks to the @ViewConfig I think), but noPreLoginEvent were thrown, so the Seam LoginListener.observePreLoginEvent wasn't called, and so my previous URL wasn't put in session. So when I logged in (immediatly after logout), I was stuck on the login page, but was logged in.

                Thanks to Brian Leathem and he's previous answer, here is what I did : in my authenticate method of my BaseAuthenticator, I called that method after authentication :

                private void overrideRedirectToLogin() {
                   
                final String PRE_LOGIN_URL = LoginListener.class.getName() + "_PRE_LOGIN_URL";
                   
                final ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
                   
                final Map<String, Object> sessionMap = externalContext.getSessionMap();
                   
                String redirectURL = (String) sessionMap.get(PRE_LOGIN_URL);

                   
                if (redirectURL == null) {
                       
                final HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
                        redirectURL
                = request.getRequestURL().toString();
                   
                }

                    sessionMap
                .put(PRE_LOGIN_URL, redirectURL.replace("/public/login.xhtml", "/pages/home.xhtml"));
                }

                With that solution, my previous URL wasn't set in session, but at least, my user is redirected to the home page.