* 请查看下面的答案以及完整的工作解决方案 *
我被 Java LDAP 连接问题困扰了好几天。
这是我连接 LDAP 服务器的方法:
public boolean authenticate(String user, String password) {
StringBuilder url = new StringBuilder("ldap://");
url.append("10.0.0.1");
url.append(":");
url.append(389);
StringBuilder securityPrincipal = new StringBuilder("uid=");
securityPrincipal.append(user);
securityPrincipal.append(",");
securityPrincipal.append("dc=XXXXX,dc=YYY,dc=ZZ");
Hashtable<String, String> env;
env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, url.toString());
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, securityPrincipal.toString());
env.put(Context.SECURITY_CREDENTIALS, password);
System.out.println(url);
System.out.println(securityPrincipal.toString());
try {
ldap = new InitialLdapContext(env, null);
} catch (NamingException e) {
e.printStackTrace();
return false;
}
return true;
}
出于安全和披露原因,我省略了 XXXXX、YYY 和 ZZ 的“dc”,并更改了 LDAP 服务器的 IP。
我在 PHP 软件 (GLPI) 中使用了相同的组合,它的效果非常好。但是,看在上帝的份上,Java 无法接受这个 LDAP 配置,总是给我这个错误:
javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(Unknown Source)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
完整的 dn 是这样的:
uid=tiagoadami,dc=XXXXX,dc=YYY,dc=ZZ
变量 user 填充为“tiagoadami”,变量“argument”填充为纯文本密码。
这很烦人。我的密码是正确的,我正在使用用户名“tiagoadami”和密码对每个应用程序进行身份验证。我现在别无选择。谁能帮我吗?
最佳答案
我很高兴得到所有评论者的帮助。如果没有你的帮助,我还会陷入困境。我发现 LDAP 服务器不允许仅使用基本 DN 进行绑定(bind)。
经过绝望的尝试,我可以使用树的完整路径进行连接:
uid=tiagoadami,ou=proto,ou=serv,ou=user,ou=collab,ou=all,dc=XXXXX,dc=YYY,dc=ZZ
而不是:
uid=tiagoadami,dc=XXXXX,dc=YYY,dc=ZZ
Sooooooooo looooooooong...通过这 2 个类我能够解决所有我的 LDAP 问题。我将它们更改为在树内部搜索并获取给定 UID 的完整 DN。这里是为有同样问题的人提供的:
TrustAllCertificatesSSLSocketFactory.java
package com.adamiworks.commonutils.ldap;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* This class accept all SSL Certificates even if it can assure its
* Certification Institute.
*
* DO NOT USE AT PRODUCTION ENVIRONMENTS
*
* @author Tiago J. Adami
*
*/
public class TrustAllCertificatesSSLSocketFactory extends SocketFactory {
private SocketFactory socketFactory;
public TrustAllCertificatesSSLSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, new TrustManager[] { new AllCertificatesTrustManager() }, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch (Exception ex) {
ex.printStackTrace(System.err); /* handle exception */
}
}
public static SocketFactory getDefault() {
return new TrustAllCertificatesSSLSocketFactory();
}
@Override
public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, i);
}
@Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, i, ia, i1);
}
@Override
public Socket createSocket(InetAddress ia, int i) throws IOException {
return socketFactory.createSocket(ia, i);
}
@Override
public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
return socketFactory.createSocket(ia, i, ia1, i1);
}
private class AllCertificatesTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
// do nothing
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
// do nothing
}
public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}
}
LdapUtils.java
package com.adamiworks.commonutils.ldap;
import java.util.Hashtable;
import java.util.Properties;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
/**
* Authenticates with LDAP Servers. Just using a single UID this class goes deep
* inside the user's tree and find the full DN for the given UID. It also allows
* to connect to servers when you don't have the certificate yet... but use this
* feature at your own risk!
*
* @author Tiago J. Adami
*
*/
public class LdapUtils {
private InitialDirContext ldap;
private String host;
private int port;
private boolean useSSL;
private boolean ignoreCertificates;
private String basedn;
public InitialDirContext getLdap() {
return ldap;
}
public boolean isIgnoreCertificates() {
return ignoreCertificates;
}
public void setIgnoreCertificates(boolean ignoreCertificates) {
this.ignoreCertificates = ignoreCertificates;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getBasedn() {
return basedn;
}
public boolean isUseSSL() {
return useSSL;
}
public void setUseSSL(boolean useSSL) {
this.useSSL = useSSL;
}
/**
* Default constructor
*
* @param host
* @param port
* @param basedn
* @param useSSL
* @param ignoreCertificates
*/
public LdapUtils(String host, int port, String basedn, boolean useSSL, boolean ignoreCertificates) {
super();
this.host = host;
this.port = port;
this.useSSL = useSSL;
this.basedn = basedn;
this.ignoreCertificates = ignoreCertificates;
}
/**
* Authenticates an user and password from LDAP credentials;
*
* @param uid
* @param password
* @return
* @throws NamingException
*/
public boolean authenticate(String uid, String password) {
try {
String url = getUrl();
String dn = this.getDnByUid(uid);
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, url);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, dn);
env.put(Context.SECURITY_CREDENTIALS, password);
if (this.useSSL) {
env.put(Context.SECURITY_PROTOCOL, "ssl");
}
if (this.useSSL && this.ignoreCertificates) {
env.put("java.naming.ldap.factory.socket", "com.adamiworks.commonutils.ldap.TrustAllCertificatesSSLSocketFactory");
}
ldap = new InitialDirContext(env);
} catch (AuthenticationException e) {
e.printStackTrace();
return false;
} catch (NamingException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Returns the url based on SSL or not
*
* @return
*/
private String getUrl() {
StringBuilder url = new StringBuilder();
url.append(this.useSSL ? "ldaps://" : "ldap://");
url.append(host);
url.append(":");
url.append(port);
return url.toString();
}
/**
* Returns the url based on SSL or not
*
* @return
*/
private String getUrlWithoutSsl() {
StringBuilder url = new StringBuilder();
url.append("ldap://");
url.append(host);
return url.toString();
}
/**
* Return LDAP authentication modes allowed by the server
*
* @param url
* @return
* @throws NamingException
*/
public Attributes getLdapAuths() throws NamingException {
// Create initial context
DirContext ctx = new InitialDirContext();
// Read supportedSASLMechanisms from root DSE
Attributes attrs = ctx.getAttributes(this.getUrl(), new String[] { "supportedSASLMechanisms" });
System.out.println(attrs);
return attrs;
}
/**
* Returns the full DN (distinct name) for a given UID
*
* @param uid
* the UID name of the user
* @return full tree path of LDAP
* @throws NamingException
*/
@SuppressWarnings("rawtypes")
public String getDnByUid(String uid) throws NamingException {
String url = this.getUrlWithoutSsl() + "/" + this.basedn;
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, url);
String ret = "uid=" + uid;
DirContext ctx = null;
try {
// Create initial context
ctx = new InitialDirContext(env);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration answer = ctx.search("", "(uid=" + uid + ")", controls);
while (answer.hasMore()) {
SearchResult sr = (SearchResult) answer.next();
ret = sr.getNameInNamespace();
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close the context when we're done
ctx.close();
}
System.out.println("FULL DN: " + ret);
return ret;
}
}
关于java - Java JNDI 连接的 LDAP 错误 49,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26920068/