Implementing single sign-on with a Tomcat valve

The RAMBLE project are potentially looking at single sign-on as a mechanism to authenticate users of their resources, including blogs. Pebble is a J2EE web application and, instead of implementing authentication itself, Pebble delegates this to the underlying web/application server. This declarative security model means that Pebble can be used with anything from in-memory and file-based realms to database and LDAP realms without changing any code. At a high level, all of the security roles and constraints are defined in the web.xml file and only at deployment time do you choose which realm implementation will be used. Many web/app servers don't support SSO out of the box, but weaving in J2EE web applications into an existing SSO infrastructure is possible.

A team at one of the clients that I recently worked for (a "major investment bank") wanted to use a blog for sharing information, almost like a team diary. One of their strategic deployment platforms includes Tomcat and after a bit of research, Pebble seemed to fit their needs. Being such a large organisation, they already have an SSO solution rolled out that all applications can (and should) make use of. This presented an interesting challenge - how do you integrate a J2EE webapp with SSO?

The way that the SSO implementation works is that all webapps are fronted by an Apache instance running a single-sign on module intercepting all requests. After an initial successful authentication, additional headers are placed into the request and the user is forwarded on to the resource they requested. With Apache doing all of the hard work, all the webapp has to do is look for the presence of the credentials. If you've implemented authentication yourself then this is easy, but how can you get this to fit into the declarative security model that many J2EE web applications use?

One idea that I came up with was to implement a filter that intercepted all requests to the webapp (after Apache has done its authentication) and create the appropriate Principal objects based upon the credentials in the HTTP request headers. While this would work, there's not an easy way to get access to the vendor specific implementation details from a Servlet filter. Creating the Principal object is easy, but what do you do with it and how do you hook it back into the server so that it becomes transparent to your webapp? To help answer this, I downloaded the Tomcat source code and looked through it to determine exactly how the security model worked and whether I really could gain access to any Tomcat specific classes by explicitly casting objects such as the HTTP request and response. After a bit of failed experimentation, I decided to change tack. The concept of intercepting the authenticated requests and creating the appropriate Principal objects was okay, but (AFAIK) it's not something that can be implemented using a Servlet filter.

After a bit more digging around, I decided to look at Tomcat's valves. Valves are essentially filters that operate outside of your web application, intercepting all requests before they are subsequently processed. Since they are specific to Tomcat, Valve implementations get to see all sorts of internal classes that are not visible by using a Servlet filter. To implement an SSO valve, first I defined a Principal class. Although it doesn't do anything except extend an existing Tomcat implementation, it serves as a way to distinguish whether the principal has been authenticated via SSO.

package sso.tomcat;

import java.util.List;

import org.apache.catalina.Realm;
import org.apache.catalina.realm.GenericPrincipal;

/**
 * Represents a Principal, authenticated by SSO.
 */
public class SSOPrincipal extends GenericPrincipal {

  public SSOPrincipal(Realm realm, String name, List roles) {
    super(realm, name, "", roles);
  }

}

Next up is an SSO security realm implementation. This is a very trivial implementation that doesn't actually perform any authentication. Instead, it's used to determine whether the principal is associated with a specific role.

package sso.tomcat;

import java.beans.PropertyChangeListener;
import java.security.Principal;
import java.security.cert.X509Certificate;

import org.apache.catalina.Container;
import org.apache.catalina.Realm;
import org.apache.catalina.realm.GenericPrincipal;

/**
 * The security realm used for SSO authentication. This doesn't actually
 * perform any authentication, but is needed for for the SSOValve.
 */
public class SSORealm implements Realm {

  private Container container;

  public Container getContainer() {
    return this.container;
  }

  public void setContainer(Container container) {
    this.container = container;
  }

  public String getInfo() {
    return "SSO realm";
  }

  public void addPropertyChangeListener(
    PropertyChangeListener propertyChangeListener) {
  }

  public Principal authenticate(String s, String s1) {
    return null;
  }

  public Principal authenticate(String s, byte[] bytes) {
    return null;
  }

  public Principal authenticate(String s, String s1,
    String s2, String s3, String s4,
    String s5, String s6, String s7) {
    return null;
  }

  public Principal authenticate(X509Certificate[] x509Certificates) {
    return null;
  }

  public boolean hasRole(Principal principal, String role) {
    if (principal instanceof SSOPrincipal) {
      // check it's a principal created by the SSO valve
      SSOPrincipal p = (SSOPrincipal)principal;
      return p.hasRole(role);
    } else {
      return false;
    }
  }

  public void removePropertyChangeListener(
    PropertyChangeListener propertyChangeListener) {
  }

}

Finally is the valve implementation itself.

package sso.tomcat;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.*;

/**
 * The Tomcat valve that transfers SSO credentials into a Principal object,
 * to provide seamless integration between SSO and the J2EE security model.
 */
public class SSOValve implements Valve {

  public String getInfo() {
    return null;
  }

  /**
   * Looks for the SSO headers in the originating request and creates a
   * Principal representing these if they exist.
   */
  public void invoke(Request request, Response response, ValveContext valveContext)
    throws IOException, ServletException {

    HttpRequest httpRequest = (HttpRequest)request;
    HttpServletRequest httpServletRequest = (HttpServletRequest)request.getRequest();
    List roles = new ArrayList();
    String username = (String)httpServletRequest.getHeader(SSO_USERNAME);
    String rolesAsString = (String)httpServletRequest.getHeader(SSO_ROLE_LIST);

    StringTokenizer tok = new StringTokenizer(rolesAsString, ",");
    while (tok.hasMoreTokens()) {
      String token = tok.nextToken().trim();
      roles.add(token);
    }

    httpRequest.setUserPrincipal(new SSOPrincipal(
      httpRequest.getContext().getRealm(), username, roles));

    // now execute all other valves
    valveContext.invokeNext(request, response);
  }

}
This valve checks for the presence of the SSO headers in the HTTP request and, if it finds them, extracts that information and creates a Principal object. Using the Tomcat APIs, this is then associated with the current HTTP request. Admittedly this is a fairly simple implementation and some hardening up would be a good idea. Still, it shows how easy it is to change how authentication is implemented and doesn't require any code changes to webapps that use declarative security. Many people say that the J2EE security model is constraining, but it can be very powerful if you choose to follow it. I think my next challenge in this area will be to hook up Tomcat to Microsoft Active Directory Server for another Pebble implementation.



Re: Implementing single sign-on with a Tomcat valve

Take a look at securityfilter.sf.net. It implements a securityfilter that looks and feels like normal container managed authentication, but with full plugability of backend authorisation implementation.

Re: Implementing single sign-on with a Tomcat valve

Simon, I hope you noticed that Tomcat already provides a valve to do single sign on? It's documented at the bottom of the valve documentation page you linked to, and configured as described at: http://jakarta.apache.org/tomcat/tomcat-5.0-doc/config/host.html#Single%20Sign%20On Also, SecurityFilter is quite popular, but (as its web page indicates) SSO is not supported.

Re: Implementing single sign-on with a Tomcat valve

Yes, I saw that but was under the impression that is was to provide an SSO experience between multiple webapps residing on the same Tomcat instance. The valve that I've built allows Tomcat to be integrated with an existing SSO infrastructure so that users have a single username/password across all applications (web and desktop) within their organisation.

Re: Implementing single sign-on with a Tomcat valve

If you need Single Sign-On functionality in J2EE you can implement it using the open source solution called JOSSO (Java Open Single Sign-On).

JOSSO Home Page : http://www.josso.org

Download JOSSO 1.1 from here

JOSSO Single Sign-On 1.1 main features include :
* 100% Java
* JAAS-based Transparent Single Sign-On across multiple applications and hosts.
* Built-in with a Pluggable Framework to allow the implementation of multiple authentication schemes and stores.
* Runs in JBoss 3.2.6 application server.
* Runs in Jakarta Tomcat 5.0.27+ .
* Provides Identity information to web applications and EJBs through the standard Servlet and EJB Security API respectively.
* Comes with a Reverse Proxy component that can be used to create n-tier Single Sign-On configurations.
* LDAP support for storing user information and credentials.
* Database support for storing user information and credentials.
* XML support for storing user information and credentials.
* Avalability of a Jakarta Tomcat + JOSSO 1.1 bundle.
* Client API for PHP. This allows to build SSO-enabled PHP applications.
* Standard Based: JAAS, Web Services/SOAP, EJB, Struts, Servlet/JSP.
* Commercial-Friendly. Released under the BSD License.

Re: Implementing single sign-on with a Tomcat valve

Thanks for the link. :-)

Re: Implementing single sign-on with a Tomcat valve

Hi Simon thanks for your write-up. I found it very helpful.

Re: Implementing single sign-on with a Tomcat valve

Hello, I am having kind of simliar requirement. In my case i had invoked custom loginmodule through a valve. However, it goes ahead and fails at hasrole() check. In has role check it wants the Prinicipal to be of instanceof JBOSSGenereicPrincipal()[this class is private]. I havent implemented my ownrealm or principal, Using SimplePrinicipal. Kindly guide, how did you configure new prinicipal and realm in JBOSS?

Re: Implementing single sign-on with a Tomcat valve

Hi Simon, Great article. I had one question though. I am not sure how the Realm is referenced/used by the Valve. Thanx, Tajdar

Re: Implementing single sign-on with a Tomcat valve

Yes, re-reading the entry it's not explicit. Basically, you plug in the realm as you do any other sort of realm and configure it for use at the container or webapp level.

Re: Implementing single sign-on with a Tomcat valve

Hi Simon, I am facing the following problem with this approach. On the web tier (servlets/jsps) everything works fine. But the caller Principal is not passed on to the EJB tier. Looks like JBoss expects the Principal to be a JBossCallerPrincipal to do the association (from Web to EJB tier).JBossCallerPrincipal can't be extended as its is package scoped (at least in JBoss3.2.6). Any ideas ? Thanx, Tajdar

Re: Implementing single sign-on with a Tomcat valve

Hi Simon, I tryed to implement single sign on with tomcat 5.5. The problem is that Internet Explorer 6 doesn't share session cookies within multiple windows. Have you got any idea? I also had to implement SSO for web service, how can I do?

Re: Implementing single sign-on with a Tomcat valve

IE does share cookies between windows but it depends on how you get the other window open. Clicking the "e" or by launching IE twice will cause two IE processes and they won't share session cookies. Simply doing Ctrl-N for a new window in an existing IE will not create another process and the session cookies are shared between windows.

Re: Implementing single sign-on with a Tomcat valve

Hello,

thank you for the source code but I'm a beginer and I want to ask you how i can put it in TOMCAT.
How can I proceed to the integration of your source code.
Thank you for helping me because I've about the same project to do at school and currently I don't know how to do it

thank you

Re: Implementing single sign-on with a Tomcat valve

Thanks for the article, very good! I think SSO is much more useful when it pulls the credentials straight from the desktop and doesn't force the user to log in to the web app at all. I'm glad you chose that route!

opensource sso alternatives

Some other alternatives.
- SSO Using JAAS & KERBEROS V5
- JOSSO (Java Open Single Sign-On)
- OpenSSO
- SPNEGO SSO
- Yale CAS
jYog

opensource sso alternatives

JOSSO is not an alternative, since it doesn't support SPNEGO (a.k.a HTTP Negotiate) thus can't take credentials from the desktop. Most of the other alternatives, from brief reading, may not support this either.

Re: Implementing single sign-on with a Tomcat valve

I'm trying to implement the valve interface but I dont seem to have it. Could you please help me? What am I missing here? a jar? Thanks a lot.

Re: Implementing single sign-on with a Tomcat valve

I would suggest you use the AuthenticatorBase from tomcat to implement it, instead of using the valve interface. If you're using JBoss you can extend the authenticator list and add yours. The tomcat jar (including sources) can be found in maven repository.

Kerberos SSO with Tomcat

Hi Simon, thanks for the good article. I'm facing what you called at the end of the article -My next challenge-. Namely weaving a webapp that has an embedded Tomcat running in an Intranet that uses Kerberos for its SSO. Any clue how can this be made?

The SecurityFilter Project Home Page

The SecurityFilter Project Home Page is something I had not seen before. Looks quite neat and has the advantage over Simon Brown's weblog - Implementing single sign-on with a Tomcat valve in that it will work with other servlet

Add a comment Send a TrackBack