来自Java JNDI Connection的LDAP错误49

时间:2014-11-13 23:00:10

标签: java authentication ldap jndi

*使用完整的工作解决方案检查以下答案

我因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)中使用了相同的组合,它就像一个魅力。但是,为了GOD的缘故,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

变量用户填充“tiagoadami”,变量“argument”填充纯文本密码。

这很烦人。我的密码是正确的,我正在使用用户名“tiagoadami”和密码对每个应用程序进行身份验证。我现在没有选择了。任何人都可以帮助我吗?

1 个答案:

答案 0 :(得分:0)

我很高兴得到所有评论的人的帮助。没有你的帮助,我会被困住。我发现LDAP服务器不允许仅与基本DN绑定。

在绝望的尝试之后,我可以使用树的完整路径进行连接:

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 ... 这两个类我能够解决 ALL 我的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;
    }

}