Tomcat 8 - LDAP:NameNotFoundException 错误代码 32,剩余名称为空字符串

Tomcat 8 - LDAP: NameNotFoundException error code 32, remaining name empty string

尝试将应用程序从 WebLogic 12.2.1 迁移到 Tomcat 8.5.4,在 Weblogic 下是一个作为 LDAP 连接的 Foreign JNDI Providers 条目已迁移到 Tomcat 下的新 Resource

在 Stack Overflow 上 this advice 之后,自定义 LdapContextFactory 已打包为 Tomcat lib 文件夹下的新 jar 文件。

在 Tomcat server.xml 文件中配置了以下 GlobalNamingResources/Resource

    <Resource name="ldapConnection" 
        auth="Container"
        type="javax.naming.ldap.LdapContext"
        factory="com.sample.custom.LdapContextFactory"
        singleton="false"
        java.naming.referral="follow"
        java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
        java.naming.provider.url="ldap://some.host:389"
        java.naming.security.authentication="simple"
        java.naming.security.principal="CN=some,OU=some,OU=some,DC=some,DC=a,DC=b"
        java.naming.security.credentials="password"
        com.sun.jndi.ldap.connect.pool="true"
        com.sun.jndi.ldap.connect.pool.maxsize="10"
        com.sun.jndi.ldap.connect.pool.prefsize="4"
        com.sun.jndi.ldap.connect.pool.timeout="30000" />

当通过 LDAP 浏览器(如 Apache Directory Studio/嵌入在 Eclipse 中的 LDAP 浏览器)浏览 LDAP 目录时,上述连接工作正常。

自定义com.sample.custom.LdapContextFactory很简单:

public class LdapContextFactory implements ObjectFactory {

    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
            throws Exception {

        Hashtable<Object, Object> env = new Hashtable<>();
        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();
            env.put(type, content);
        }
        return new InitialLdapContext(env, null);
    }
}

但是,在启动时 Tomcat 抛出以下异常:

07-Sep-2016 15:04:01.064 SEVERE [main] org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans Exception processing Global JNDI Resources
 javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of:
    ''
 ]; remaining name ''
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3160)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3081)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2888)
    at com.sun.jndi.ldap.LdapCtx.c_listBindings(LdapCtx.java:1189)
    at com.sun.jndi.toolkit.ctx.ComponentContext.p_listBindings(ComponentContext.java:592)
    at com.sun.jndi.toolkit.ctx.PartialCompositeContext.listBindings(PartialCompositeContext.java:330)
    at com.sun.jndi.toolkit.ctx.PartialCompositeContext.listBindings(PartialCompositeContext.java:317)
    at javax.naming.InitialContext.listBindings(InitialContext.java:472)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:136)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:145)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:110)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.lifecycleEvent(GlobalResourcesLifecycleListener.java:82)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:94)
    at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:401)
    at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:345)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:784)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:655)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:355)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:495)

Similar 问题和调查表明 LDAP DN 无效,但是:

问题:这是将 Foreign JNDI Providers 条目从 WebLogic 迁移到 Tomcat 的正确方法吗?如何修复剩余名称为空的无效 LDAP DN 条目?难不成是哪里少了baseDN配置?


更新
正如评论所建议的那样,将 LdapContextFactory 更改为以下内容时会发生同样的错误:

public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
        throws Exception {

    Hashtable<Object, Object> env = new Hashtable<>();
    Reference reference = (Reference) obj;
    Enumeration<RefAddr> references = reference.getAll();

    String providerUrl = "no valid URL";

    while (references.hasMoreElements()) {
        RefAddr address = references.nextElement();
        String type = address.getType();
        String content = (String) address.getContent();

        switch (type) {
        case Context.PROVIDER_URL:
            env.put(Context.PROVIDER_URL, content);
            providerUrl = content;
            break;

        default:
            env.put(type, content);
            break;
        }
    }

    InitialLdapContext context = null;
    Object result = null;
    try {
        context = new InitialLdapContext(env, null);

        LOGGER.info("looking up for " + providerUrl);
        result = context.lookup(providerUrl);
    } finally {
        if (context != null) {
            context.close();
        }
    }
    LOGGER.info("Created new LDAP Context");
    return result;
}

更改通过日志记录确认,以确保正确部署。

所涉及的侦听器默认在 server.xml 文件的顶部定义为

<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

并且无法根据 official documentation 禁用:

The Global Resources Lifecycle Listener initializes the Global JNDI resources defined in server.xml as part of the Global Resources element. Without this listener, none of the Global Resources will be available.


Tomcat 版本 8.5.57.0.69 也发生同样的情况:只需添加新的全局资源如上和提供上述工厂的附加 jar,将抛出指向空剩余名称的异常。

通过使用问题中提供的第一个工厂实现将 LDAP 模式 DN 附加到 java.naming.provider.url 属性,堆栈跟踪消失了。

在此上下文中使用的 LDAP 客户端的屏幕截图下方是嵌入在 Eclipse 中的 Apache Directory Studio / LDAP 浏览器,从中可以简单地使用问题的初始值浏览相关的 LDAP。

通过将 Root 元素的模式 DN 附加到连接 URL,异常消失并且 LDAP 资源现在通过 JNDI 在 Tomcat 8.

中共享

故障排除结果的更多详细信息

在 Tomcat 中,8 个全局资源通过全局资源侦听器处理,GlobalResourcesLifecycleListener,默认定义在 server.xml 文件中。这样一个侦听器 invokes 一个 context.listBindings("") 在 bean 创建上,因此有效地浏览了 LDAP 目录。

这种初始浏览很可能是 Tomcat 和 WebLogic 之间的区别,后者仅在需要时通过 JNDI 查找 LDAP,因此通过直接查询,而不是在启动时使用通用查询。因此,在 Tomcat 中,LDAP url 需要进一步的详细信息,即作为其 url 的一部分的配置略有不同,以直接指向有效的基本 DN。

来自官方WebLogic documentation:

On start up, WebLogic Server attempts to connect to the JNDI source. If the connection is successful, WebLogic Server sets up the requested objects and links in the local JNDI tree, making them available to WebLogic Server clients.

因此,连接比 listBindings:

更简单

Enumerates the names bound in the named context, along with the objects bound to them. The contents of any subcontexts are not included.