« Welcome | Main | Maven EAR Artefacts... »

Form based authentification using Facelets

Yesterday I found out how to configure my JEE Web Application with form-based login. In general it is not difficulty to secure a JEE Web Application with form based login. This is explained on lot of websites like this one: http://www.developinjava.com/readarticle.php?article_id=6

The problem I stumbled into was that my Web Application uses the facelets framework. So I have a lot of general layout parts like css and images which I wanted to use also in my Login Form and also some help pages which should be accessible without authentification.

To divide the web application into a restricted and a public area (where no authentification is needed) is easy:

in the web.xml you can specify the secured JSP pages separated in a folder e.g:

 <security-constraint>
    <web-resource-collection>
      <web-resource-name>restricted</web-resource-name>
      <url-pattern>/faces/pages/*</url-pattern>
       <http-method>GET</http-method>
      <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>org.imixs.ACCESSLEVEL.AUTHORACCESS</role-name>
    </auth-constraint>
  </security-constraint>

The Login.xhtml which I placed outside my secured /pages folder makes use of facelets technology and looks as followed:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:z="http://www.qualcomm.com/jsf/core"
    xmlns:c="http://java.sun.com/jstl/core"
    xmlns:fn="http://java.sun.com/jsp/jstl/functions">

<ui:composition template="/layout/layout.xhtml">
    <ui:define name="content">
        <form
            method="post"
            action="#{facesContext.externalContext.requestContextPath}/j_security_check">
        <table>
            <tr>
                <td>User name:</td>
                <td><input type="text" name="j_username" /></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type="password"  name="j_password" /></td>
            </tr>
            <tr>
                <td><input type="submit"  value="Login" /></td>
            </tr>
        </table>
        </form>
    </ui:define>
</ui:composition>
</html>

/layout/layout.xhtml is accessible without authentification as this page is not placed inside my secured /pages/ folder. So the login.xhtml page looks perfect styled!

The real problem was how to jump into a secured page? I first have had a navigation.jsp page with a lot of jsf command links like:

<h:commandLink actionListener="#{workitemListBean.doRefresh}"
                    action="show_workitemlist">

My Navigation rule for "show_workitemlist" points to a secured page in the /pages folder.

faces-config.xml: 

......
    <navigation-case>
            <from-outcome>show_worklist</from-outcome>
            <to-view-id>/pages/workflow/worklist.jsp</to-view-id>
    </navigation-case>

But these commandLinks will not work because the JSF Framework will invoke all backing beans which are connected to the target page behind the navigation rule "show_workitmelist". And all my backing beans invoke a lot of EJBs which are secured using declarative JEE security.

So my command link did not show up the login.xhtml form as expected but throws a lot of "permission denied" exceptions :-(

The trick is to forward the anonymous user direct to the faces/ URL to access the secured page without JSF Navigation Rules. So I replaced my <h:commandLink> with a simple anchor tag:

<a href="#{facesContext.externalContext.requestContextPath}/faces/pages/workflow/worklist.jsp">
                <h:outputText value="#{global.login}" /></a>

So the result of these anchor tag is that the non authenticated user requests a secured page from my /faces/pages URL Pattern as defined in the web.xml.

The webContainer will invoke my Login Page, authenticate the user and after a successful login the JSF Framework starts up and builds all the necessary things to view the page with all backing-beans and EJBs. As the user is now authenticated everything works perfect as JSF has all the principal stuff and user informations to invoke my secured EJBs!

So: do not use commandLinks to login an anonymous user in a JSF Application inside a Facelets layouted login page.