Java连接Active Directory LDAP出现java.net.SocketException: Connection or outbound has closed错误。

8

晚上好,

我最近遇到了一个错误,已经寻找了网络上的大量解决方案,但是仍无法修复该错误。

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class LDAPtest {
public static void main(String[] args) {
    
    try {
        
        String keystorePath = "C:/Program Files/Java/jdk-13.0.2/lib/security/cacerts";
        System.setProperty("javax.net.ssl.keyStore", keystorePath);
         
        System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
        
        Hashtable<String, String> ldapEnv = new Hashtable<>(); 
        ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
        ldapEnv.put(Context.PROVIDER_URL, "ldaps://localhost:10636");
        ldapEnv.put(Context.SECURITY_AUTHENTICATION,"simple");
        ldapEnv.put(Context.SECURITY_PRINCIPAL,"uid=admin,ou=system");
        ldapEnv.put(Context.SECURITY_CREDENTIALS,"secret");
        
        DirContext connection = new InitialDirContext(ldapEnv);
        System.out.println("Benvenuto " + connection);
        NamingEnumeration enm = connection.list("");

        while (enm.hasMore()) {
            System.out.println(enm.next());
        }

        enm.close();
        connection.close();
        
        
    }catch(Exception e) {
        e.printStackTrace();
    }
    
}

这段代码在未进行SSL测试时实际上是可以工作的,只需要将

ldapEnv.put(Context.PROVIDER_URL, "ldaps://localhost:10636");

使用

ldapEnv.put(Context.PROVIDER_URL, "ldap://localhost:10389");

我使用Apache Directory Studio进行LDAP服务器设置,并按照此教程进行操作以使LDAPS正常工作:http://directory.apache.org/apacheds/basic-ug/3.3-enabling-ssl.html
所以我创建了证书,甚至将其安装并使用keytool导入到cacerts中。我为所选端口(10636)启用了端口转发,但仍然遇到此异常:
javax.naming.CommunicationException: simple bind failed: localhost:10636 [Root exception is 
java.net.SocketException: Connection or outbound has closed]
at java.naming/com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:219)
at java.naming/com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2795)
at java.naming/com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:320)
at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxFromUrl(LdapCtxFactory.java:225)
at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:189)
at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:243)
at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:154)
at java.naming/com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:84)
at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:730)
at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
at java.naming/javax.naming.InitialContext.init(InitialContext.java:236)
at java.naming/javax.naming.InitialContext.<init>(InitialContext.java:208)
at java.naming/javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:130)
at Prova3.main(Prova3.java:31)
Caused by: java.net.SocketException: Connection or outbound has closed
at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:1246)
at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)
at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142)
at java.naming/com.sun.jndi.ldap.Connection.writeRequest(Connection.java:398)
at java.naming/com.sun.jndi.ldap.Connection.writeRequest(Connection.java:371)
at java.naming/com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:359)
at java.naming/com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:214)
... 13 more

谢谢您提前帮忙。

我在JDK8中也遇到了同样的问题。正如https://dev59.com/mrfna4cB1Zd3GeqPu44r中所回答的那样,这似乎不是JDK版本的问题。 - Shoner Sul
2个回答

11

对于谷歌员工:

simple bind failed 错误几乎总是与 SSL 连接相关。

使用 nc 或者 telnet 检查客户端和远程主机之间是否可以建立连接,并且确认端口是否正确。

使用 SSLPoke.java(一个简单的 Java 类来检查 SSL 连接),检查证书是否被正确导入和使用,并检查正确的 TLS 版本。使用类似这样的命令: java -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2 -Djavax.net.debug=all SSLPoke google.com 443 > log.txt 2>&1

请查看以下内容:

  • Warning: no suitable certificate found - continuing without client authentication = 检查您是否设置了 javax.net.ssl.trustStore
  • Fatal (HANDSHAKE_FAILURE): Couldn't kickstart handshaking = 可能是不匹配的 TLS 版本
  • 还要检查您的中间 CA 是否过期

1
"连接或出站已关闭"是一个常见的异常,可能有几个可能的原因。为了找到主要原因,您应该启用SSL调试来启动您的应用程序。您可以通过在启动时添加以下VM参数来实现这一点:

-Djavax.net.debug=ssl,handshake

在我的情况下,启用了SSL调试模式后,我得到了"CertificateException: No name matching localhost found"的错误信息,这已经非常明确了。真正的问题是自签名证书中配置的主机名与访问主机时使用的主机名不匹配。在握手过程中,不仅验证证书的有效性,还要验证主机名的匹配性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接