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

A_D*_*teo 17 java tomcat weblogic ldap tomcat8

尝试将应用程序从WebLogic 12.2.1迁移到Tomcat 8.5.4,Weblogic下的内容是作为LDAP连接的外部JNDI提供程序的条目已迁移到新的ResourceTomcat下.

遵循Stack Overflow上的建议,自定义LdapContextFactory已打包为jarTomcat lib文件夹下的新文件.

在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" />
Run Code Online (Sandbox Code Playgroud)

通过嵌入在Eclipse中的Apache Directory Studio/LDAP Browser等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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在启动时,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)
Run Code Online (Sandbox Code Playgroud)

类似的问题和调查表明LDAP DN无效,但是:

  • 相同的LDAP配置可通过LDAP客户端正常工作
  • 实际上没有执行搜索,在启动时Tomcat会抛出此异常而不进行任何查询
  • 错误提示为空字符串''作为remaining name,因此不是真的没有发现的东西,显然

问题:这是将外部JNDI提供程序条目从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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
Run Code Online (Sandbox Code Playgroud)

并且不能根据官方文档禁用:

全局资源生命周期监听器初始化server.xml作为全局资源元素的一部分定义的全局JNDI资源.没有这个监听器,全局资源都不可用.


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

A_D*_*teo 2

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

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

在此输入图像描述

通过将根元素的架构 DN 附加到连接 URL,异常消失了,LDAP 资源现在通过 Tomcat 8 中的 JNDI 共享。


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

在 Tomcat 8 中,全局资源是通过全局资源监听器(GlobalResourcesLifecycleListener默认在文件中定义的)进行处理的server.xml。这样的侦听器调用on context.listBindings("")bean 创建,从而有效地浏览 LDAP 目录。

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

来自官方WebLogic文档

启动时,WebLogic Server 尝试连接到 JNDI 源。如果连接成功,WebLogic Server 将在本地 JNDI 树中设置请求的对象和链接,使它们可供 WebLogic Server 客户端使用。

因此,连接比 简单得多listBindings

枚举命名上下文中绑定的名称以及绑定到它们的对象。不包括任何子上下文的内容。