Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to configure LDAP as JNDI Resource in Tomcat

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?

like image 284
rjcarr Avatar asked Oct 23 '12 23:10

rjcarr


1 Answers

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.

like image 142
cassiomolin Avatar answered Sep 21 '22 19:09

cassiomolin