How to do Spring Security authentication from within a JSF form

jsf-2spring-security

I created a simple JSF login page, and I'm trying to integrate it with spring security.

Here is the form element from login.xhtml

    <h:form>

     <h:outputLabel value="User Id:" for="userId"/>

     <h:inputText id="j_username" label="User Id"
     required="true"   value="#{loginBean.name}" >

     </h:inputText>

     <h:outputLabel value="Password: "  for ="password"/>

     <h:inputSecret id="j_password" value="#{loginBean.password}" />

     <h:commandButton value="Submit" action="#{j_spring_security_check}" />

 </h:form>

But the rendered html page has something like the below. Take a look at the form action and the input tag's names

The form element

  <form id="j_idt6" name="j_idt6" method="post" 
    action="/jsfproject2/faces/login.xhtml"
       enctype="application/x-www-form-urlencoded">

And the input tags

   User Id:</label><input id="j_idt6:j_username" type="text"
     name="j_idt6:j_username" />

Now I want form action to be /j_spring_security_check and input boxes to be 'j_username' and j_password

How can we achieve this ?

Best Solution

There are two options for Spring Security to work.

Use prependId="false" on a JSF form

As <h:form> is a naming container, it prepends id of its children with the specified id, or the autogenerated id, so as Spring Security expects ids to remain unchainged, just don't prepend the ids:

 <h:form prependId="false">
     <h:outputLabel value="User Id: " for="userId" />
     <h:inputText id="j_username" label="User Id" required="true" value="#{loginBean.name}" />
     <h:outputLabel value="Password: "  for ="password" />
     <h:inputSecret id="j_password" value="#{loginBean.password}" />
     <h:commandButton value="Submit" action="#{loginBean.login}" />
</h:form>

Note that #{j_spring_security_check} is a wrong action method: it needs to be #{loginBean.login} with the following contents:

public String login() {
    //do any job with the associated values that you've got from the user, like persisting attempted login, etc.
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext extenalContext = facesContext.getExternalContext();
    RequestDispatcher dispatcher = ((ServletRequest)extenalContext.getRequest()).getRequestDispatcher("/j_spring_security_check");
    dispatcher.forward((ServletRequest)extenalContext.getRequest(), (ServletResponse)extenalContext.getResponse());
    facesContext.responseComplete();
    return null;
}

Basically, all you need to do is dispatch to /j_spring_security_check and have j_username and j_password as request parameters.

Use plain HTML form

Basically, there's no particular need to mess with JSF form on this issue, in case you don't need to do some extra things apart from authentication, and plain HTML form is sufficient for Spring Security to do its job.

<form action="/j_spring_security_check" method="POST">
    <label for="j_username">User Id: </label>
    <input id="j_username" name="j_username" type="text" />
    <label for="j_password">Password: </label>
    <input id="j_password" name="j_password" type="password"/>
    <input type="submit" value="Submit"/>
</form>
Related Question