I have an ldap server that I'm using to authenticate users within a tomcat web application. I'm using the JNDIRealm and it's configured within a context file and this works great.
I'll also need to search the ldap for user information. I've figured out how to do this with the "jndi method" and I have it working fine outside of tomcat by creating my own jndi context using a hashtable. However, instead of configuring the jndi properties in code, I'd like to create a JNDI Rsource in my context file right next to the Realm configuration.
I'm thinking I would do something like this:
<Resource
name="ldap"
auth="Container"
type="com.sun.jndi.ldap.LdapCtxFactory"
java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
java.naming.provider.url="ldap://localhost:389"
java.naming.security.authentication="simple"
java.naming.security.principal="uid=rjcarr,dc=example"
java.naming.security.credentials="abc123"
/>
But either tomcat tells me the resource can't be created or when I try to initialize it with something like this:
Context initctx = new InitialContext();
DirContext ctx = (DirContext) initctx.lookup("java:comp/env/ldap");
Tomcat tells me the "Cannot create resource instance". I've also added the correct resource-ref in my web.xml file, so I don't think that's the problem.
Since LDAP is being used with the JNDI method I'm assuming it should be able to be configured as a Resource, right? What am I missing?
This answer is a bit late, but probably it'll be useful for other users. It's based on EJP's answer.
The following solution was tested on Apache Tomcat 7.
If you need, you can replace LdapContext
with DirContext
.
Create an ObjectFactory
Create a class which implements ObjectFactory
to instantiate a LdapContext
:
public class LdapContextFactory implements ObjectFactory {
public Object getObjectInstance(Object obj, Name name, Context nameCtx,
Hashtable<?, ?> environment) throws Exception {
Hashtable<Object, Object> env = new Hashtable<Object, Object>();
Reference reference = (Reference) obj;
Enumeration<RefAddr> references = reference.getAll();
while (references.hasMoreElements()) {
RefAddr address = references.nextElement();
String type = address.getType();
String content = (String) address.getContent();
switch (type) {
case Context.INITIAL_CONTEXT_FACTORY:
env.put(Context.INITIAL_CONTEXT_FACTORY, content);
break;
case Context.PROVIDER_URL:
env.put(Context.PROVIDER_URL, content);
break;
case Context.SECURITY_AUTHENTICATION:
env.put(Context.SECURITY_AUTHENTICATION, content);
break;
case Context.SECURITY_PRINCIPAL:
env.put(Context.SECURITY_PRINCIPAL, content);
break;
case Context.SECURITY_CREDENTIALS:
env.put(Context.SECURITY_CREDENTIALS, content);
break;
default:
break;
}
}
LdapContext context = new InitialLdapContext(env, null);
return context;
}
}
Define your resource
Add the following to your context.xml
, referencing the factory and defining the values to create a LdapContext
instance:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
...
<Resource name="ldap/LdapResource" auth="Container"
type="javax.naming.ldap.LdapContext"
factory="com.company.LdapContextFactory"
singleton="false"
java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
java.naming.provider.url="ldap://127.0.0.1:389"
java.naming.security.authentication="simple"
java.naming.security.principal="username"
java.naming.security.credentials="password" />
</Context>
If you need to add more attributes/values to your resource, consider updating your ObjectFactory
created above to read these new attributes/values.
Use your resource
Inject your resource wherever you need:
@Resource(name = "ldap/LdapResource")
private LdapContext bean;
Or look it up:
Context initialContext = new InitialContext();
LdapContext ldapContext = (LdapContext)
initialContext.lookup("java:comp/env/ldap/LdapResource");
See more
Apache Tomcat's documentation explains how to add custom resource factories.
You're making it up. The type of a Tomcat resource must be a class that implements javax.naming.spi.ObjectFactory. See the Tomcat documentation for custom resources.