The Source for Java Technology Collaboration
User: Password:



Ed Burns

Ed Burns's Blog

Repost: Using JAAS with JSF

Posted by edburns on March 07, 2006 at 01:33 PM | Comments (13)

The content and ideas in this blog entry are taken from my upcoming McGraw Hill Osborne book JavaServer Faces: The Complete Reference, which I am co-authoring with Chris Schalk. In the book, we have a chapter on Securing JavaServer Faces applications. This excerpt shows how the extensible design of JavaServer Faces can be used to allow JAAS to provide authentication to a JSF Web application. JAAS Authorization with JSF is also covered in the book, but not in this excerpt.

We will conclude this chapter by showing how to enhance the example application by leveraging the standard security infrastructure of the Java platform. From its inception, the Java platform has treated security as a first class concern. Indeed, one of the first benefits of Java was to securely bring dynamic behavior to web deployed applications. Over the years, the implementation and API to security has evolved, but the core principals have improved and become steadily more secure. Therefore, choosing to build your application managed security on top of the standard Java security features is a very safe bet.

A term often applied to Java security is JAAS, which is short for Java Authentication and Authorization Service. JAAS started out as an optional package in JDK 1.3 but has become a core part of the Java platform as of JDK 1.4. As the name implies, JAAS covers the first two of the three main aspects of security: authentication and authorization. Let's explore one way to integrate JAAS style authentication and authorization into the application

Using JAAS Authentication in the example application

While it would certainly be possible to call into the JAAS layer directly from the example application logic, for example, from the UserRegistry bean, a more re-usable solution is to encapsulate the JAAS interface in a custom ActionListener. This approach de-couples the security completely from your application and takes advantage of the intended use of the ActionListener extension hook.

The mechanics of providing such an ActionListener are described in Chapter 11, but let's review briefly here. The first step is to modify the faces-config.xml file for the example reusable component library so that it includes the action-listener declaration, as shown here.

<application>
  <action-listener>
    com.jsfcompref.examplecomponents.util.JAASActionListener
  </action-listener>
</application>

Then, leverage the decorator pattern, as described in Chapter 10, to delegate most of the work to the "real" ActionListener by providing a constructor that saves a reference to it. Following the constructor, the processAction( ) method must be implemented, as described below.

private ActionListener parent = null;
    
public JAASActionListener(ActionListener parent) {
  this.parent = parent;
}

public void processAction(ActionEvent event)
  throws AbortProcessingException {
  FacesContext context = FacesContext.getCurrentInstance();
  UIOutput comp = null;
  String userid = null, password = null;
  JAASHelper jaasHelper = new JAASHelper();
  
  // Check to see if they are on the login page.
  boolean onLoginPage = (-1 != context.getViewRoot().getViewId().
      lastIndexOf("login")) ? true : false;
  if (onLoginPage) {
    if (null != (comp = (UIOutput) 
      context.getViewRoot().findComponent("form:userid"))) {
      userid = (String) comp.getValue();
    }
    if (null != (comp = (UIOutput) 
      context.getViewRoot().findComponent("form:password"))) {
      password = (String) comp.getValue();
    }
    // If JAAS authentication failed
    if (!jaasHelper.authenticate(userid, password)) {
      context.getApplication().getNavigationHandler().
          handleNavigation(context, null, "login");
      return;
    }
    else {
      // Subject must not be null, since authentication succeeded
      assert(null != jaasHelper.getSubject());
      // Put the authenticated subject in the session.
      context.getExternalContext().getSessionMap().put(JAASSubject, 
          jaasHelper.getSubject());
    }
  }
  parent.processAction(event);
}

The first thing to note is that part of the usage contract for JAASActionListener is the requirement that the username and password components be nested inside a UIForm named "form", and be named "userid" and "password" respectively. This expedient measure allows the JAASActionListener to easily extract the user-provided values for username and password so that they can be passed on to the JAASHelper class. The second thing to note about the usage contract is the requirement that the application provide a navigation rule for the outcome "login" that causes the user to be directed to the login page if the authentication failed. In the failure case, processAction( ) is not called until after redirecting to the "login" outcome using NavigationHandler. If authentication succeeded, the Subject is stored in the session for later access. The java.security.Subject is the Java class that represents the user to the runtime. (We'll cover Subject in greater detail in the section on JAAS authentication.) Finally, the parent processAction( ) method is called to do the normal action handling. Note that this causes the existing application managed authentication, as described in Chapter 9, to take place. A production quality implementation would probably remove the application managed authentication in favor of using JAAS, rather than just supplementing it, as we have done here.

Let's examine the JAASHelper class.

public class JAASHelper {
  
  LoginContext loginContext = null;

  public JAASHelper() {
  }
  
  public boolean authenticate(String userid, String password) {
    boolean result = false;
    try {
      loginContext = new LoginContext("FileLogin", 
          new LoginCallback(userid, password));
      loginContext.login();
      result = true;
    }
    catch (LoginException e) {
      // A production quality implementation would log this message
      result = false;
    }
    return result;
  }
  
  public Subject getSubject () {
    Subject result = null;
    if (null != loginContext) {
      result = loginContext.getSubject();
    }
    return result;
  }
  
  public static class LoginCallback implements CallbackHandler {
    private String userName = null;
    private String password = null;
    
    public LoginCallback(String userName, String password) {
      this.userName = userName;
      this.password = password;
    }
    
    public void handle(Callback[] callbacks) {
      for (int i = 0; i< callbacks.length; i++) {
        if (callbacks[i] instanceof NameCallback) {
          NameCallback nc = (NameCallback)callbacks[i];
          nc.setName(userName);
        } else if (callbacks[i] instanceof PasswordCallback) {
          PasswordCallback pc = (PasswordCallback)callbacks[i];
          pc.setPassword(password.toCharArray());
        }
      }
    }
  }
}

The authenticate( ) method uses the class java.security.auth.login.LoginContext to perform the login. The login( ) method of this class will throw a LoginException if the login fails for any reason. This exception is caught by authenticate( ) and it responds by setting result to false. false. If no exception is thrown, result is set to true. authenticate( ) ends by returning the value of result.

The two arguments to the LoginContext constructor are the most important part of this example. The first, the literal string "FileLogin", refers to an implementation of the javax.security.auth.spi.LoginModule interface. This interface is implemented by a provider of a particular implementation of authentication technology, for example JNDI, LDAP, or database. In this example, we use a free software implementation called "tagish" that provides a simple file based authentication scheme. The implementation comes from John Gardner and can be found at http://free.tagish.net/jaas/. Providing a LoginModule implementation is beyond the scope of this chapter, but we must illustrate how to use one, once it has been provided. This is the beauty of JAAS, the authentication technology itself is separated from the rest of the system. In other words, if you want to plug in LDAP, do it by providing a custom LoginModule.

The JVM is made aware of the existence of a LoginModule implementation either through a -D flag, or via a modification to the JAVA_HOME/jre/lib/java.security file. In our case, we use the former option: -Djava.security.auth.login.config== D:/Projects/example/chapterCode/ch14/example/src/resources/tagish.login Note the use of forward slashes instead of the standard Windows backslashes. Also note the "==" instead of just one "=". The format of this file is prescribed by JAAS:

FileLogin
{
  com.tagish.auth.FileLogin required
pwdFile="D:/Projects/example/chapterCode/ch14/example/src/resources/passwd";
};

The FileLogin identifier must match the argument to the LoginContext constructor. The first element inside the FileLogin declaration is the fully qualified class name of the class implementing LoginModule. In our application, we have bundled tagish.jar, which contains this class, into the WEB-INF/lib directory of the application. The required flag tells the system that the login must succeed, and whether it succeeds or fails, the login must still proceed down the LoginModule chain. Other valid values for this flag are requisite, sufficient, and optional and are described in the javadocs for the class javax.security.auth.login.Configuration. The pwdFile argument is an implementation specific parameter to the code in tagish.jar that tells it where to find its password file. The format of this file is also implementation specific and for the example app looks like this:

username:MD5 Hash Of Password:group*
The specific file for the example follows:
# Passwords for com.tagish.auth.FileLogin

jfitness:5a64edabc9358c603103053a3c600a88:user
stiger:40be4e59b9a2a2b5dffb918c0e86b3d7:user
guest:084e0343a0486ff05530df6c705c8bb4:user
jake:1200cf8ad328a60559cf5e7c5f46ee6d:user:manager

Obviously, a simple MD5 hash of the password is not at all secure and a production quality implementation would use an actual encryption algorithm. For the purposes of security, MD5 is just as secure as Base64 encoding, described earlier in the chapter, which is to say, not at all secure. A handy MD5 hash calculator can be found at http://bfl.rctek.com/tools/?tool=hasher. Note that user jake is a member of the user and manager groups, while all the other users are simply members of the user group. Groups will come into play in the next section.

The second argument to the LoginContext constructor is an implementation of the javax.security.auth.callback.CallbackHandler interface. The LoginCallback implementation saves the username and password ultimately originating from the userid and password components in its constructor and uses standard boilerplate code to propagate them to the JAAS system.

Technorati Tags:

Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Hi Ed, great post! it is a good example for your book. i suggest you look towards the jGuard project(http://www.jguard.net) which integrate (with the help of JAAS) all this stuff and many more features . it is based on a servlet filter which can be used on jsf-based applications, struts-based or any kind of web framework. it provides a taglib to display part of pages based on role and permissions (Role based access control design), enable permissions, roles and users changes on the fly, handle role inheritance, handle FORM,BASIC, CLIENT-CERT authentication and so on... cheers, Charles(jGuard team).

    Posted by: diabolo512 on March 08, 2006 at 04:46 AM

  • Ed, What about all the declarative security features of the servlet spec? That made sense at the time (years ago) but since then I've read several articles about using JAAS with struts, servlets, and now JSF.

    Why would someone choose to ignore security via the deployment descriptor and write to the JAAS api?
    Taylor

    Posted by: tcowan on March 08, 2006 at 07:55 AM

  • Hello Taylor,

    Several reasons, really. Foremost, increasing the set of integration options. If you already have an investment in JAAS, it's nice to be able to leverage it. Secondarily, the JAAS architecture is more low level than that provided by the container. That said, we do have a spec issue to allow getUserPrincipal & co in JSF 1.2: See issue 110.

    Ed (JSF co spec lead)

    Posted by: edburns on March 08, 2006 at 08:02 AM

  • Two questions:

    1. Can we assume that this book will cover all changes included with JSF 1.2?
    2. Does the book have a chapter or a section about using JSF with Facelets?

    Thanks...

    Posted by: frank1russo on March 08, 2006 at 08:43 AM

  • Hello Mr. Russo,

    I'm very happy to report that the answers are yes and yes.

    Ed

    Posted by: edburns on March 08, 2006 at 08:52 AM

  • // Check to see if they are on the login page.
      boolean onLoginPage = (-1 != context.getViewRoot().getViewId().
          lastIndexOf("login")) ? true : false;
    
    Doh :-)

    Posted by: brucechapman on March 08, 2006 at 03:46 PM

  • I would like to test this JAAS integration in my application. Do i have to use a phaseListener to detect that the JAASSubject is in the session or not and redirect to the login page if it is not the case ? Why use an ActionListener can we do the same JAAS integration in a phaseListener ?

    Posted by: fvisticot on May 04, 2006 at 02:54 PM

  • A production quality implementation would probably remove the application managed authentication in favor of using JAAS, rather than just supplementing it, as we have done here." Could you elaborate please ? Are you stating that it would be possible to use sort of a "declarative" web.xml JAAS setup with JSF.

    I am not sure how to implement this option. Basically, it would be nice to get more details on this issue, since your book is not going to be out till August 2006. Thanks!

    Posted by: anonymouse on May 24, 2006 at 08:07 AM

  • Hello Ed, In the JSF world how does the JAAS Principal get set on the request ? Meaning, when I call the ExternalContext.getUserPrincipal(), what do I need to do for it to return a non-null Principal object ? I see your examples (here as well as your book) adding the Subject to the ExternalContext.sessionMap. Is that the only way to use JAAS with JSF or is there a way to set the Principla on the ExternalContext (couldn't find any Set method on it). Thanks, Kannan

    Posted by: kxk on January 24, 2007 at 09:28 AM

  • Hello Ed, I'm have problems with the findComponent method. I believe the problem is that I've created a Page Fragment to layout the text boxes to enter login information. I've created that fragment because I want to add this box on every page of my site. If the user is signed in, this box should show "Welcome, ". Since this is a fragment, i'm having troubles accessing the textfields. I think this is because the viewRoot is not my fragment, but the page it is in. Any ideas? Thanks, Kristof.

    Posted by: kristof_taveirne on April 16, 2007 at 07:19 AM

  • 
    KT> Hello Ed, I'm have problems with the findComponent method. I believe
    KT> the problem is that I've created a Page Fragment to layout the text
    KT> boxes to enter login information. I've created that fragment because
    KT> I want to add this box on every page of my site. If the user is
    KT> signed in, this box should show "Welcome, ". Since this is
    KT> a fragment, i'm having troubles accessing the textfields. I think
    KT> this is because the viewRoot is not my fragment, but the page it is
    KT> in. Any ideas? Thanks, Kristof.
    
    findComponent is a bit of an odd one.  It doesn't do exactly what the
    name suggests.  Please read the javadoc more closely.  In JSF 1.2, we
    introduced invokeOnComponent() to address this problem.  If you're in
    JSF 1.1, you may have to write your own method that traverses the tree
    looking for a node.
    
    

    Posted by: edburns on April 16, 2007 at 08:17 AM

  • has anyone converted this code (specifically the chapter 15 code) over to facelets? (specifically the taglibs)

    Posted by: qvanegeren on June 14, 2007 at 03:08 PM

  • Hello Ed. I went thru your Book. Great Info But i do have the same Question which kannan posted here. I use Websphere I got the JAASSubject how do i set that into JSF so that when i use ExternalContext.getUserPrincipal() it will give the pricipal which i set. In your example we are just putting in Session variable and using it. Thanks in Advance - Dhamothar

    Posted by: dhamotharkannan on March 18, 2008 at 05:10 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds