There is a 2nd edition of this post: Standalone Tomcat with jBoss (2nd Edition)!
This tutorial desribes, how to install and configure a standalone Tomcat, so that a deployed webapp can authenticate against LDAP and connect to a jBoss passing the credentials in every call of an EJB via remote interface , so that the business application can authenticate against the same LDAP, too. The configuration of the jBoss seems to be a more common and better documented task and will be covered in another tutorial, which I will link here later, as soon as I have written it.
WARNING: Please don’t use this solution in a productive system, but for testing purpose only. The custom LdapExtLoginModule presented here exposes the credentials of all online users to all classes using the same class loader! I will add a blog post, as I find a solution for production systems.
The tutorial has been successfully tested with the following versions of third party libraries:
- Tomcat 6.0.20
- jBoss 5.0.1.GA
The following steps are sufficient advices for the impatient reader, in order to run the tomcat standalone with a connection to jboss. A more detailed description about what is going on here can be found in using JAAS login modules from jboss in tomcat. Here are the instructions:
- download tomcat:
- decompress the file
- edit
$CATALINA_HOME/bin/catalina.sh
:
export JAVA_OPTS="$JAVA_OPTS -Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config -Djava.naming.provider.url=localhost:1399"
- We’re using JAAS for authentication. We declare two login modules. The
ClientLoginModule
builds the security context for EJB calls, so that the application server gets the credentials for authentication. TheLdapExtLoginModule
authenticates the user with LDAP and builds the subject for Tomcat. In order to configure JAAS create/edit the file$CATALINA_HOME/conf/jaas.config
:
someapp { // create security context for jboss org.jboss.security.ClientLoginModule required; multi-threaded="true"; // Login module for tomcat org.someorg.security.LdapExtLoginModule required java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory" java.naming.security.authentication="simple" java.naming.provider.url="ldap://<your_ldap_host>:<your_ldap_port>" //We use anonymous authentication to our LDAP here bindDN="" bindCredential="" // Base DN, where we search our users baseCtxDN="ou=people,dc=someorg,dc=org" // Our users log in via uid baseFilter="(uid={0})" // Our roles are situated in the following subtree rolesCtxDN="ou=Roles,dc=someorg,dc=org" // A member of a group is identified/referenced by its full DN here roleFilter="(member={1})" roleAttributeID="cn" // We assume flat roles roleRecursion="-1"; };
- edit
$CATALINA_HOME/conf/server.xml
:- comment out all existing
<Realm>
-tags - add the following block:
- comment out all existing
<Realm className="org.apache.catalina.realm.JAASRealm" appName="someapp" useContextClassLoader="false" userClassNames="org.jboss.security.SimplePrincipal" roleClassNames="org.jboss.security.SimpleGroup" />
- Above we used a class called
org.someorg.security.LdapExtLoginModule
. This class must be on the class path of tomcat and jboss ($CATALINA_HOME/lib
and$JBOSS_SERVER_CONFIG_HOME/lib
). Here is an implementation of the class:
package org.someorg.security; public class LdapExtLoginModule extends org.jboss.security.auth.spi.LdapExtLoginModule { private String credential; private Principal identity; private static Map<string, Object> credentials = new HashMap<string, Object>(); public static Object getCredential(String username) { return credentials.get(username); } @Override protected Group[] getRoleSets() throws LoginException { List<group> groups = new LinkedList<group>(); Enumeration<? extends Principal> roles = super.getRoleSets()[0].members(); while(roles.hasMoreElements()) { groups.add(new SimpleGroup(roles.nextElement().getName())); } return groups.toArray(new Group[0]); } @Override @SuppressWarnings("unchecked") public boolean login() throws LoginException { // See if shared credentials exist if( super.login() == true ) { String[] info = getUsernameAndPassword(); this.identity = new SimplePrincipal(info[0]); this.credential = info[1]; return true; } return false; } @Override public boolean commit() throws LoginException { // SecurityAssociationActions.setPrincipalInfo(identity, credential); boolean success = super.commit(); if(success) { credentials.put(identity.getName(), credential); } return success; } @Override public boolean logout() throws LoginException { credentials.remove(identity.getName()); } }
- We can load a session bean in a servlet like this (the login has to be done at least once per request, because a new request might run in another thread, so a call to a session bean would be done with the user anonymous):
String user = httpRequest.getRemoteUser(); String credential = (String)LdapExtLoginModule.getCredential(user); Properties env = new Properties(); env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory"); env.setProperty(Context.SECURITY_PRINCIPAL, user); env.setProperty(Context.SECURITY_CREDENTIALS, credential); env.setProperty("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces"); InitialContext ctx = new InitialContext(env); MySessionBean sb = (MySessionBean)ctx.lookup("myapp/MySessionBean/remote"); ...
- In order to get everything running jbosssx.jar and jbossall-client.jar (part of the jboss package) has to be on the classpath. So get it and put it into the
$CATALINA_HOME/lib
folder.
This is great stuff for a Jboss-Junkie like me! Keep it goin’…
“Please don’t use this solution in a productive system,”
HI… Did you wrotte a “productive simple” ? Where is the link ?
Regards.
I don’t have a solution for a productive system by now. But I’m working out a solution using REST-services to access a jboss from an external tomcat. I will write a concept post soon.
Hi Jonny
I realy didn’t undertands how standalone web container propagates JAAS autentication to ejb container !!!! I’ve read a lot of stuff and I realized that I must config and duplicate roles inside war and ejb xmls….and duplicate the specific way how this container will autenticate and authorized (XML, JDBC, LDAP etc….)
I’m right ??
Best Regards
There is no propagation. The Login name and credential is stored in a static class member (of the login module) and later reused for a login during creation of the
InitialContext
initializing the connection to the JNDI of the jboss.