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
Re: Implementing single sign-on with a Tomcat valve
Re: Implementing single sign-on with a Tomcat valve
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
Re: Implementing single sign-on with a Tomcat valve
Re: Implementing single sign-on with a Tomcat valve
Re: Implementing single sign-on with a Tomcat valve
Re: Implementing single sign-on with a Tomcat valve
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
Simon is a hands-on software architect who works within 