Java教程是为JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言更改了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发行说明了解所有JDK版本的新功能、增强功能以及已删除或弃用选项的信息。
除了SASL身份验证外,大多数LDAP服务器允许通过SSL访问其服务。对于LDAP v2服务器来说,SSL尤其有用,因为v2协议不支持SASL身份验证。
启用SSL的服务器通常有两种方式支持SSL。最基本的方式是,服务器支持SSL端口,除了正常(未保护)端口之外。服务器支持SSL的另一种方式是通过使用Start TLS扩展(RFC 2830)进行。此选项仅适用于LDAP v3服务器,并在部分中详细描述。
JDK中的LDAP服务提供程序在与LDAP服务器通信时默认使用普通套接字。要请求使用SSL套接字,请将Context.SECURITY_PROTOCOL属性设置为"ssl"。
在下面的示例
中,LDAP服务器在端口636上提供SSL。要运行此程序,您必须在LDAP服务器上的端口636上启用SSL。此过程通常由目录的管理员执行。
服务器要求:LDAP服务器必须配置有X.509 SSL服务器证书并启用SSL。通常,您必须先从证书颁发机构(CA)获取服务器的签名证书。然后,根据目录供应商的说明启用SSL。不同的供应商有不同的工具来完成这个过程。
对于Oracle Directory Server, v5.2,请使用管理控制台中的"Manage Certificates"工具生成证书签名请求(CSR)。将CSR提交给CA以获取X.509 SSL服务器证书。使用管理控制台,将证书添加到服务器的证书列表中。如果CA的证书尚未在服务器的受信任CA列表中,请安装该CA的证书。使用管理控制台中的"Configuration"选项卡启用SSL。在左窗格中选择服务器,在右窗格中选择"Encryption"选项卡。选中"Enable SSL for this server"和"Use this cipher family: RSA"复选框,确保您添加的服务器证书在证书列表中。
客户端要求:您需要确保客户端信任您将使用的LDAP服务器。您必须在JRE的可信证书数据库中安装服务器的证书(或其CA的证书)。下面是一个示例。
# cd JAVA_HOME/lib/security # keytool -import -file server_cert.cer -keystore jssecacerts
有关如何使用安全工具的信息,请参阅Security路径。有关JSSE的信息,请参阅Java Secure Socket Extension (JSSE) 参考指南。
// 设置创建初始上下文的环境 Hashtable<String, Object> env = new Hashtable<String, Object>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://localhost:636/o=JNDITutorial"); // 指定SSL env.put(Context.SECURITY_PROTOCOL, "ssl"); // 以S. User的身份验证,密码为"mysecret" env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires,o=JNDITutorial"); env.put(Context.SECURITY_CREDENTIALS, "mysecret"); // 创建初始上下文 DirContext ctx = new InitialDirContext(env); // ... 与ctx一起进行有用的操作
注意:如果您使用SSL连接到未使用SSL的服务器上的端口,则程序将挂起。同样,如果您使用普通套接字连接到服务器的SSL套接字,则应用程序将挂起。这是SSL协议的特点。
您可以通过使用LDAPS URL而不是使用Context.SECURITY_PROTOCOL属性来请求使用SSL。LDAPS URL类似于LDAP URL,只是URL方案为"ldaps"而不是"ldap"。它指定了与LDAP服务器通信时使用SSL。
在以下示例
中,LDAP服务器在端口636上提供SSL。要运行此程序,您必须在LDAP服务器上的端口636上启用SSL。
// 设置创建初始上下文的环境 Hashtable<String, Object> env = new Hashtable<String, Object>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); // 指定LDAPS URL env.put(Context.PROVIDER_URL, "ldaps://localhost:636/o=JNDITutorial"); // 以S. User的身份验证,密码为"mysecret" env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, "cn=S. User, ou=NewHires, o=JNDITutorial"); env.put(Context.SECURITY_CREDENTIALS, "mysecret"); // 创建初始上下文 DirContext ctx = new InitialDirContext(env); // ... 与ctx一起进行有用的操作
LDAPS URL可以在任何接受LDAP URL的地方使用。有关LDAP和LDAPS URL的详细信息,请查看JNDI教程。
SSL在比LDAP更低的层次上提供身份验证和其他安全服务。如果已在SSL层次上进行了身份验证,则LDAP层可以通过使用External SASL机制来使用SSL中的身份验证信息。
以下示例
与先前的SSL示例
相似,不同之处在于它使用External SASL身份验证而不是使用简单身份验证。通过使用External,您无需提供任何主体或密码信息,因为它们将从SSL中获取。
服务器要求:此示例要求LDAP服务器允许基于证书的客户端身份验证。此外,LDAP服务器必须信任(CA的)接收到的客户端证书,并且必须能够将客户端证书中的所有者可分辨名称映射到其所知道的主体。请按照您的目录供应商的说明执行这些任务。
客户端要求:此示例要求客户端具有X.509 SSL客户端证书。此外,该证书必须作为密钥库文件中的第一个密钥条目存储。如果此条目受密码保护,则其必须与密钥库具有相同的密码。有关JSSE密钥库的更多信息,请参见Java Secure Socket Extension (JSSE) 参考指南。
// 为创建初始上下文设置环境 Hashtable<String, Object> env = new Hashtable<String, Object>(11); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://localhost:636/o=JNDITutorial"); // 主体和凭据将从连接中获取 env.put(Context.SECURITY_AUTHENTICATION, "EXTERNAL"); // 指定SSL env.put(Context.SECURITY_PROTOCOL, "ssl"); // 创建初始上下文 DirContext ctx = new InitialDirContext(env); ...
要以使用客户端证书进行身份验证的方式运行此程序,您必须提供(作为系统属性)包含客户端证书的密钥库的位置和密码。以下是如何运行该程序的示例。
java -Djavax.net.ssl.keyStore=MyKeystoreFile \ -Djavax.net.ssl.keyStorePassword=mysecret \ External
如果您没有提供密钥库,程序将使用匿名身份验证运行,因为在SSL中不存在客户端凭证。
此示例展示了基于证书的客户端身份验证的最基本方法。更高级的方法可以通过编写和使用自定义套接字工厂来实现,该套接字工厂以更灵活的方式访问客户端证书,例如使用LDAP目录。下一节将介绍如何在您的JNDI应用程序中使用自定义套接字工厂。
在使用SSL时,默认情况下,LDAP提供程序将使用套接字工厂javax.net.ssl.SSLSocketFactory创建一个SSL套接字与服务器进行通信,使用默认的JSSE配置。JSSE可以通过各种方式进行自定义,详细信息请参阅Java安全套接字扩展(JSSE)参考指南。然而,有时这些自定义是不够的,您需要对LDAP服务提供程序使用的SSL套接字或普通套接字有更多控制权。例如,您可能需要能够绕过防火墙的套接字,或者使用非默认缓存/检索策略的JSSE套接字用于其信任和密钥库。要设置LDAP服务提供程序使用的套接字工厂实现,请将"java.naming.ldap.factory.socket"属性设置为套接字工厂的完全限定类名。该类必须实现javax.net.SocketFactory抽象类并提供getDefault()方法的实现,该方法返回套接字工厂的实例。请参阅Java安全套接字扩展(JSSE)参考指南。
下面是一个产生普通套接字的自定义套接字工厂
的示例。
public class CustomSocketFactory extends SocketFactory { public static SocketFactory getDefault() { System.out.println("[获取默认套接字工厂]"); return new CustomSocketFactory(); } ... }
请注意,此示例每次创建新的CustomSocketFactory实例时都会创建一个新的LDAP连接。这对于某些应用程序和套接字工厂可能是合适的。如果您想重用相同的套接字工厂,getDefault()应返回一个单例。
要在JNDI程序中使用此自定义套接字工厂,请设置"java.naming.ldap.factory.socket"属性,如以下示例
所示。
// 设置创建初始上下文的环境 Hashtable<String, Object> env = new Hashtable<String, Object>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial"); // 指定套接字工厂 env.put("java.naming.ldap.factory.socket", "CustomSocketFactory"); // 创建初始上下文 DirContext ctx = new InitialDirContext(env); // ... 使用ctx进行一些有用的操作
"java.naming.ldap.factory.socket"属性对于在每个上下文基础上设置套接字工厂非常有用。控制LDAP服务提供程序使用的套接字的另一种方法是通过使用java.net.Socket.setSocketImplFactory()设置整个程序中使用的所有套接字的套接字工厂。使用此方法的灵活性较差,因为它影响所有套接字连接,而不仅仅是LDAP连接,因此应谨慎使用。