4

I am trying - with JSF 2.0 - to implement in a neat way the login/remember me/logout management. Since the traditional <form action="j_security_check" ... way lacks of flexibility I decided to follow a different path, but I found a problem.

Declarative security is properly set both in the application server through <security-domain> and in web.xml through <security-constraint>, <login-config> and <form-login-page>.

The login page:

<h:form id="loginForm"> 
    <h:panelGrid columns="2" cellspacing="5">
        <h:outputText value="Username" />
        <h:inputText value="#{loginBean.username}" />
        <h:outputText value="Password:" />
        <h:inputText value="#{loginBean.password}" />
        <h:outputLabel value=""/>
        <h:commandButton value="Login" action="#{loginBean.login}" />
    </h:panelGrid>      
</h:form>

And the simple LoginBean#login():

public String login( )
{
    HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance( ).getExternalContext( ).getRequest( );        
    try {
        request.login( username, password );
    }
    catch ( ServletException e ) {
        FacesContext.getCurrentInstance().addMessage( "Unknown login...
        return null;
    }       
    return "i_dont_know_where_you_were_going";
}

Everything works fine, but after a successful login I don't know how to forward the user to its original request. Since the login page is automatically interposed between the client request and "any" secured resource I need a way to understand where to redirect the action. request.getRequestURL( ) doesn't help, probably because of a RequestDispatcher#forward() (which overwrites the request url) intervention. Do you think this is an appropriate way to manage the login process? If so, any hint about the problem?

Thanks a lot!

Fabio
  • 131
  • 3
  • 8

3 Answers3

7

Add something like the following line to your login view. It stores the requested page during the login.

<f:param name="redirect" value="#{requestScope['javax.servlet.forward.request_uri']}" />

Then get the requested uri in your login bean.

FacesContext context = FacesContext.getCurrentInstance();
String redirect = context.getExternalContext().getRequestParameterMap().get("redirect");

Add ?faces-redirect=true to the string and return it.

Robin
  • 7,527
  • 6
  • 51
  • 82
2

The above answer worked perfectly, just pointing out to the unwary .. f:param name should be inside something like the commandButton ...

<p:commandButton value="Entrar" icon="ui-icon-disk" action="#{userMB.login}" update="avisos,mensagens" ajax="false">
<f:param name="redirect" value="#{requestScope['javax.servlet.forward.request_uri']}" />
</ p: commandButton>
Camilla
  • 479
  • 5
  • 14
0

This seems to work only when placed between the button (Mojarra 2.2, Glassfish 4). You will need these two <f:param tags javax.servlet.forward.request_uri and javax.servlet.forward.query_string to ensure you accurately redirect back to all types of URL (with or without query strings). Make sure you have something similar to the snippet below in your login page i.e. the page you specified in your web.xml for login (<form-login-page>/login.xhtml</form-login-page>).

<h:commandButton value="Login" action="#{securityController.login()}">
  <f:param name="redirect" value="#{requestScope['javax.servlet.forward.request_uri']}" />
  <f:param name="query_string" value="#{requestScope['javax.servlet.forward.query_string']}" />
</h:commandButton>

You can then retrieve both parameters in the backing bean after form submission as below

public String login() {
    try {
       String nextPage = "/index.xhtml"; // have a default value in case the user goes to login page directly.
       ExternalContext ctx = FacesContext.getCurrentInstance().getExternalContext();
       Map<String, String> map = ctx.getRequestParameterMap();
       String redirect = map.get("redirect");
       String queryString = map.get("query_string");
       HttpServletRequest request = (HttpServletRequest) ctx.getRequest();
       request.login(this.username, this.password);
       // here login is successful otherwise it would've thrown exception
       // now let's check the kind of URL our user was going to 
       if (redirect != null && !redirect.isEmpty()) {  // if redirect is null, return the default page
       // please use faces-redirect = true to ensure URL change in the browser
          if (queryString != null) {
              nextPage = redirect + "?" + queryString + "&faces-redirect=true";
          } else {  // there is no query string, nextPage = redirect
              nextPage = redirect + "&faces-redirect=true";
          }
          // Caveat: You may not need the next lines of code. 
          // I discovered that the `redirect` string has my context path  
          // value in it and so it was'nt redirecting. Remove the context path
          if (nextPage.contains(request.getContextPath())) {
            nextPage = nextPage.substring(request.getContextPath().length());
          }
      }

    } catch (ServletException ex) {
      Logger.getLogger(SecurityController.class.getName()).log(Level.SEVERE, null, ex);
      // invalid username or password
      return "/login.xhtml?failed=true";  // or login error page
    }
 return nextPage;
}

I hope this helps someone.

Tunde Michael
  • 354
  • 4
  • 8