Java 在LDAP服务器中对用户进行身份验证

Java 在LDAP服务器中对用户进行身份验证,java,ldap,openldap,Java,Ldap,Openldap,我使用以下配置配置了OpenLDAP服务器: version: 1 # Entry 1: dc=unixmen,dc=com dn: dc=unixmen,dc=com dc: unixmen o: unixmen objectclass: top objectclass: dcObject objectclass: organization # Entry 2: cn=ServerAdmins,dc=unixmen,dc=com dn: cn=ServerAdmins,dc=unixmen

我使用以下配置配置了OpenLDAP服务器:

version: 1

# Entry 1: dc=unixmen,dc=com
dn: dc=unixmen,dc=com
dc: unixmen
o: unixmen
objectclass: top
objectclass: dcObject
objectclass: organization

# Entry 2: cn=ServerAdmins,dc=unixmen,dc=com
dn: cn=ServerAdmins,dc=unixmen,dc=com
cn: ServerAdmins
gidnumber: 501
objectclass: posixGroup
objectclass: top

# Entry 3: cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com
dn: cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com
cn: rcbandit
gidnumber: 501
givenname: rcbandit
homedirectory: /home/users/rcbandit
objectclass: inetOrgPerson
objectclass: posixAccount
objectclass: top
sn: rcbandit
uid: rcbandit
uidnumber: 1000
userpassword: {MD5}2FeO34RYzgb7xbt2pYxcpA==
我创建了一个Java代码来搜索凭证:

public class SAuth
{

    public static void main(String[] args)
    {

        Hashtable env = new Hashtable(11);
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://192.168.1.177:389");
        //env.put(Context.SECURITY_AUTHENTICATION, "simple");
        //env.put(Context.SECURITY_PRINCIPAL, "cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com");
        //env.put(Context.SECURITY_CREDENTIALS, "qwerty");

        // Enable connection pooling
        env.put("com.sun.jndi.ldap.connect.pool", "true");

        try
        {
            LdapContext ctx = new InitialLdapContext(env, null);
            ctx.setRequestControls(null);
            NamingEnumeration<?> namingEnum = ctx.search("cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com", "(objectclass=*)", getSimpleSearchControls());
            while (namingEnum.hasMore())
            {
                SearchResult result = (SearchResult) namingEnum.next();
                Attributes attrs = result.getAttributes();
                System.out.println(attrs.get("cn"));
                System.out.println(attrs.get("gidnumber"));
                System.out.println(attrs.get("givenname"));
                System.out.println(attrs.get("homedirectory"));
                System.out.println(attrs.get("objectclass"));
                System.out.println(attrs.get("objectclass"));
                System.out.println(attrs.get("objectclass"));
                System.out.println(attrs.get("sn"));
                System.out.println(attrs.get("uid"));
                System.out.println(attrs.get("uidnumber"));
                System.out.println(attrs.get("userpassword"));

            }
            namingEnum.close();
            ctx.close();
        }
        catch (NamingException e)
        {
            e.printStackTrace();
        }

    }

    private static SearchControls getSimpleSearchControls()
    {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        searchControls.setTimeLimit(30000);
        //String[] attrIDs = {"objectGUID"};
        //searchControls.setReturningAttributes(attrIDs);
        return searchControls;
    }
}

验证用户名和密码的正确方法是什么?

看起来您可以通过未经验证的连接获取信息。但是,您需要在LDAP中执行
bind()
操作来执行身份验证

绑定操作的功能是允许在客户端和服务器之间交换身份验证信息。绑定操作应被视为“身份验证”操作

更多信息

创建
InitialLdapContext
时,将执行代码中的绑定操作。但是,您需要具有要进行身份验证(已注释掉)的凭据和安全主体。目前,您正在通过未经验证的渠道阅读允许的信息

正确的方法是使用实例化
InitialLdapContext
与要验证的主体和凭据绑定,并捕获失败的主体和凭据的
javax.naming.AuthenticationException

env.put(Context.PROVIDER_URL, "ldap://XXX.XXX.XXX.XXX:XXX");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com");
env.put(Context.SECURITY_CREDENTIALS, "xxxx");
..
LdapContext ctx = new InitialLdapContext(env, null);

}
catch(AuthenticationException ex) {
...
}

如果您不使用JNDI,这将很容易做到。所有JavaLDAP SDK都允许在同一个LDAP连接上绑定一个方法。我强烈建议您使用现代、最新的LDAP SDK,而不要使用JNDI。现在我们喜欢

为什么只有一个连接不需要在LDAP服务器上进行配置

下面是一个示例,它使用管理员进行搜索,然后将其绑定为返回的条目:

package com.willeke.samples.ldap.jndi;

import java.util.*;

import javax.naming.*;
import javax.naming.directory.*;

/**
 * 
 * <p>
 * Title: BasicJNDISearch
 * </p>
 * 
 * <p>
 * Description: Provides a sample for performing JNDI Searches
 * </p>
 * 
 * @author Jim Willeke
 * @version 1.0
 */
public class BasicAdminSearchBind
{
    public BasicAdminSearchBind(String[] args)
    {
    super();

    try
    {
        BasicAdminSearchBind.doBasicSearch(args);
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
    }

    /**
     * 
     * @param stid
     *            String - Standard ID (uid)
     * @throws Exception
     *             -
     */
    public static void doBasicSearch(String[] args) throws Exception
    {
    System.out.println("Performing LDAP Search with:");
    System.out.println("    ldapHostName = " + args[0]);
    System.out.println("        ldapPort = " + args[1]);
    System.out.println("          bindDn = " + args[2]);
    System.out.println("       bindDnPwd = " + args[3]);
    System.out.println("      searchBase = " + args[4]);
    System.out.println("          filter = (" + args[5] + "=" + args[6] + ")");
    System.out.println("          Scope: = SUBTREE_SCOPE");
    // Get the context for the admin account
    DirContext adminCtx = getDirContext(args[0], args[1], args[2], args[3]);
    SearchControls constraints = new SearchControls();
    // Set the Scope of the search
    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
    // Create the filter from args
    String filter = "(" + args[5] + "=" + args[6] + ")";
    // Search for objects with those matching attributes
    NamingEnumeration<?> answer = adminCtx.search(args[4], filter, constraints);
    //formatResults(answer);
    SearchResult sr = (SearchResult) answer.next();
    String userDN = sr.getNameInNamespace();
    //bind as returned entry
    try
    {
        DirContext userCtx = bindAsEntry(args[0], args[1],  userDN, "Secret Password");
        System.out.println("We are now bound as the User: "+ userDN);
        // we could do something with the userCtx here.
        userCtx.close();
    }
    catch (Exception e)
    {
       System.err.println("We failed to make a bind as " + userDN + "\n" + e.getMessage());
    }
    adminCtx.close();
    }

    /**
     * 
     * @param ldapHostName
     * @param ldapPost
     * @param bindDn
     * @param bindDnPwd
     * @return
     * @throws NamingException 
     * @throws Exception
     */
    private static DirContext bindAsEntry(String ldapHostName, String ldapPost, String bindDn, String bindDnPwd) throws NamingException 
    {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://" + ldapHostName + ":" + ldapPost);
    env.put(Context.SECURITY_PRINCIPAL, bindDn);
    env.put(Context.SECURITY_CREDENTIALS, bindDnPwd);
    // Create the initial context
    DirContext ctx = new InitialDirContext(env);
    return ctx;
    }

    /**
     * Generic method to obtain a reference to a DirContext
     * 
     * @param ldapHostName
     * @param ldapPost
     * @param bindDn
     * @param bindDnPwd
     */
    public static DirContext getDirContext(String ldapHostName, String ldapPost, String bindDn, String bindDnPwd) throws Exception
    {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://" + ldapHostName + ":" + ldapPost);
    env.put(Context.SECURITY_PRINCIPAL, bindDn);
    env.put(Context.SECURITY_CREDENTIALS, bindDnPwd);
    // Create the initial context
    DirContext ctx = new InitialDirContext(env);
    return ctx;
    }

    /*
     * Generic method to format the NamingEnumeration returned from a search.
     */
    public static void formatResults(NamingEnumeration<?> enumer) throws Exception
    {
    int count = 0;
    try
    {
        while (enumer.hasMore())
        {
        SearchResult sr = (SearchResult) enumer.next();
        System.out.println("SEARCH RESULT:" + sr.getName());
        formatAttributes(sr.getAttributes());
        System.out.println("====================================================");
        count++;
        }
        System.out.println("Search returned " + count + " results");
    }
    catch (NamingException e)
    {
        e.printStackTrace();
    }
    }

    /*
     * Generic method to format the Attributes .Displays all the multiple values of each Attribute in the Attributes
     */
    public static void formatAttributes(Attributes attrs) throws Exception
    {
    if (attrs == null)
    {
        System.out.println("This result has no attributes");
    }
    else
    {
        try
        {
        for (NamingEnumeration<?> enumer = attrs.getAll(); enumer.hasMore();)
        {
            Attribute attrib = (Attribute) enumer.next();

            System.out.println("ATTRIBUTE :" + attrib.getID());
            for (NamingEnumeration<?> e = attrib.getAll(); e.hasMore();)
            {
            Object value = e.next();
            boolean canPrint = isAsciiPrintable(value);
            if (canPrint)
            {
                System.out.println("\t\t        = " + value);
            }
            else
            {
                System.out.println("\t\t        = <-value is not printable->");
            }
            }
        }
        }
        catch (NamingException e)
        {
        e.printStackTrace();
        }
    }
    }

    /**
     * Check to see if this Object can be printed.
     * 
     * @param obj
     * @return
     */
    public static boolean isAsciiPrintable(Object obj)
    {
    String str = null;
    try
    {
        str = (String) obj;
    }
    catch (Exception e)
    {
        return false;
        // TODO Auto-generated catch block e.printStackTrace();
    }
    if (str == null)
    {
        return false;
    }
    int sz = str.length();
    for (int i = 0; i < sz; i++)
    {
        if (isAsciiPrintable(str.charAt(i)) == false)
        {
        return false;
        }
    }
    return true;
    }

    /**
     * Used by isAsciiPrintable(Object obj)
     * 
     * @param ch
     * @return
     */
    public static boolean isAsciiPrintable(char ch)
    {
    return ch >= 32 && ch < 127;
    }

    /**
     * Does a simple search on the LDAP Directory
     * 
     * String ldapHostName = args[0]; String ldapPort = args[1]; String bindDn = args[2]; String bindDnPwd = args[3]; String searchBase = args[4]; // String searchScope=args[4]; String searchAttribute = args[5];
     * String searchAttributeValue = args[6];
     * 
     * @param args
     * 
     */
    public static void main(String[] args)
    {
    if (args.length == 7)
    {
        BasicAdminSearchBind basicjndisearch = new BasicAdminSearchBind(args);
    }
    else
    {
        System.out.println("\nYou must provide ldapHostName, ldapPort, bindDn, bindDnPwd, searchBase, searchAttribute and searchAttributeValue on the command line!\n");
    }
    }
}
package com.willeke.samples.ldap.jndi;
导入java.util.*;
导入javax.naming.*;
导入javax.naming.directory.*;
/**
* 
*
*标题:基本搜索
*

* * *描述:提供用于执行JNDI搜索的示例 *

* *@作者吉姆·威勒克 *@version 1.0 */ 公共类BasicAdminSearchBind { 公共BasicAdminSearchBind(字符串[]args) { 超级(); 尝试 { BasicAdminSearchBind.doBasicSearch(args); } 捕获(例外情况除外) { 例如printStackTrace(); } } /** * *@param stid *字符串-标准ID(uid) *@抛出异常 * - */ 公共静态void doBasicSearch(字符串[]args)引发异常 { System.out.println(“使用:”)执行LDAP搜索; System.out.println(“ldapHostName=“+args[0]); System.out.println(“ldapPort=“+args[1]); System.out.println(“bindDn=“+args[2]); System.out.println(“bindDnPwd=“+args[3]); System.out.println(“searchBase=“+args[4]); System.out.println(“过滤器=(“+args[5]+”=“+args[6]+”)); System.out.println(“范围:=子树范围”); //获取管理员帐户的上下文 DirContext adminCtx=getDirContext(args[0],args[1],args[2],args[3]); SearchControls约束=新的SearchControls(); //设置搜索范围 约束.setSearchScope(SearchControls.SUBTREE_范围); //从args创建筛选器 字符串筛选器=“(“+args[5]+”=“+args[6]+””); //搜索具有这些匹配属性的对象 NamingEnumeration answer=adminCtx.search(参数[4],过滤器,约束); //结果(答案); SearchResult sr=(SearchResult)answer.next(); 字符串userDN=sr.getNameInNamespace(); //绑定为返回条目 尝试 { DirContext userCtx=bindAsEntry(args[0],args[1],userDN,“秘密密码”); System.out.println(“我们现在被绑定为用户:“+userDN”); //我们可以在这里用userCtx做点什么。 userCtx.close(); } 捕获(例外e) { System.err.println(“绑定为“+userDN+”\n“+e.getMessage()”失败); } adminCtx.close(); } /** * *@param ldapHostName *@param ldapPost *@param bindDn *@param bindDnPwd *@返回 *@NamingException *@抛出异常 */ 私有静态DirContext bindAsEntry(字符串ldapHostName、字符串ldapPost、字符串bindDn、字符串bindDnPwd)引发NamingException { Hashtable env=新的Hashtable(11); put(Context.INITIAL\u Context\u工厂,“com.sun.jndi.ldap.LdapCtxFactory”); env.put(Context.PROVIDER_URL,“ldap://”+ldapHostName+“:”+ldapPost); 环境put(Context.SECURITY_PRINCIPAL,bindDn); 环境put(Context.SECURITY_凭证,bindDnPwd); //创建初始上下文 DirContext ctx=新的初始DirContext(env); 返回ctx; } /** *获取对DirContext的引用的泛型方法 * *@param ldapHostName *@param ldapPost *@param bindDn *@param bindDnPwd */ 公共静态DirContext getDirContext(字符串ldapHostName、字符串ldapPost、字符串bindDn、字符串bindDnPwd)引发异常 { Hashtable env=新的Hashtable(11); put(Context.INITIAL\u Context\u工厂,“com.sun.jndi.ldap.LdapCtxFactory”); env.put(Context.PROVIDER_URL,“ldap://”+ldapHostName+“:”+ldapPost); 环境put(Context.SECURITY_PRINCIPAL,bindDn); 环境put(Context.SECURITY_凭证,bindDnPwd); //创建初始上下文 DirContext ctx=新的初始DirContext(env); 返回ctx; } /* *设置搜索返回的NamingEnumeration格式的通用方法。 */ 公共静态void formatResults(NamingEnumeration枚举器)引发异常 { 整数计数=0; 尝试 { while(enumer.hasMore()) { SearchResult sr=(SearchResult)枚举器。下一步(); System.out.println(“搜索结果:+sr.getName()); formatAttributes(sr.getAttributes()); System.out.println(“===============================================================================”); 计数++; } System.out.println(“搜索返回”+计数+结果”); } 捕获(NamingE例外)
package com.willeke.samples.ldap.jndi;

import java.util.*;

import javax.naming.*;
import javax.naming.directory.*;

/**
 * 
 * <p>
 * Title: BasicJNDISearch
 * </p>
 * 
 * <p>
 * Description: Provides a sample for performing JNDI Searches
 * </p>
 * 
 * @author Jim Willeke
 * @version 1.0
 */
public class BasicAdminSearchBind
{
    public BasicAdminSearchBind(String[] args)
    {
    super();

    try
    {
        BasicAdminSearchBind.doBasicSearch(args);
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
    }

    /**
     * 
     * @param stid
     *            String - Standard ID (uid)
     * @throws Exception
     *             -
     */
    public static void doBasicSearch(String[] args) throws Exception
    {
    System.out.println("Performing LDAP Search with:");
    System.out.println("    ldapHostName = " + args[0]);
    System.out.println("        ldapPort = " + args[1]);
    System.out.println("          bindDn = " + args[2]);
    System.out.println("       bindDnPwd = " + args[3]);
    System.out.println("      searchBase = " + args[4]);
    System.out.println("          filter = (" + args[5] + "=" + args[6] + ")");
    System.out.println("          Scope: = SUBTREE_SCOPE");
    // Get the context for the admin account
    DirContext adminCtx = getDirContext(args[0], args[1], args[2], args[3]);
    SearchControls constraints = new SearchControls();
    // Set the Scope of the search
    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
    // Create the filter from args
    String filter = "(" + args[5] + "=" + args[6] + ")";
    // Search for objects with those matching attributes
    NamingEnumeration<?> answer = adminCtx.search(args[4], filter, constraints);
    //formatResults(answer);
    SearchResult sr = (SearchResult) answer.next();
    String userDN = sr.getNameInNamespace();
    //bind as returned entry
    try
    {
        DirContext userCtx = bindAsEntry(args[0], args[1],  userDN, "Secret Password");
        System.out.println("We are now bound as the User: "+ userDN);
        // we could do something with the userCtx here.
        userCtx.close();
    }
    catch (Exception e)
    {
       System.err.println("We failed to make a bind as " + userDN + "\n" + e.getMessage());
    }
    adminCtx.close();
    }

    /**
     * 
     * @param ldapHostName
     * @param ldapPost
     * @param bindDn
     * @param bindDnPwd
     * @return
     * @throws NamingException 
     * @throws Exception
     */
    private static DirContext bindAsEntry(String ldapHostName, String ldapPost, String bindDn, String bindDnPwd) throws NamingException 
    {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://" + ldapHostName + ":" + ldapPost);
    env.put(Context.SECURITY_PRINCIPAL, bindDn);
    env.put(Context.SECURITY_CREDENTIALS, bindDnPwd);
    // Create the initial context
    DirContext ctx = new InitialDirContext(env);
    return ctx;
    }

    /**
     * Generic method to obtain a reference to a DirContext
     * 
     * @param ldapHostName
     * @param ldapPost
     * @param bindDn
     * @param bindDnPwd
     */
    public static DirContext getDirContext(String ldapHostName, String ldapPost, String bindDn, String bindDnPwd) throws Exception
    {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://" + ldapHostName + ":" + ldapPost);
    env.put(Context.SECURITY_PRINCIPAL, bindDn);
    env.put(Context.SECURITY_CREDENTIALS, bindDnPwd);
    // Create the initial context
    DirContext ctx = new InitialDirContext(env);
    return ctx;
    }

    /*
     * Generic method to format the NamingEnumeration returned from a search.
     */
    public static void formatResults(NamingEnumeration<?> enumer) throws Exception
    {
    int count = 0;
    try
    {
        while (enumer.hasMore())
        {
        SearchResult sr = (SearchResult) enumer.next();
        System.out.println("SEARCH RESULT:" + sr.getName());
        formatAttributes(sr.getAttributes());
        System.out.println("====================================================");
        count++;
        }
        System.out.println("Search returned " + count + " results");
    }
    catch (NamingException e)
    {
        e.printStackTrace();
    }
    }

    /*
     * Generic method to format the Attributes .Displays all the multiple values of each Attribute in the Attributes
     */
    public static void formatAttributes(Attributes attrs) throws Exception
    {
    if (attrs == null)
    {
        System.out.println("This result has no attributes");
    }
    else
    {
        try
        {
        for (NamingEnumeration<?> enumer = attrs.getAll(); enumer.hasMore();)
        {
            Attribute attrib = (Attribute) enumer.next();

            System.out.println("ATTRIBUTE :" + attrib.getID());
            for (NamingEnumeration<?> e = attrib.getAll(); e.hasMore();)
            {
            Object value = e.next();
            boolean canPrint = isAsciiPrintable(value);
            if (canPrint)
            {
                System.out.println("\t\t        = " + value);
            }
            else
            {
                System.out.println("\t\t        = <-value is not printable->");
            }
            }
        }
        }
        catch (NamingException e)
        {
        e.printStackTrace();
        }
    }
    }

    /**
     * Check to see if this Object can be printed.
     * 
     * @param obj
     * @return
     */
    public static boolean isAsciiPrintable(Object obj)
    {
    String str = null;
    try
    {
        str = (String) obj;
    }
    catch (Exception e)
    {
        return false;
        // TODO Auto-generated catch block e.printStackTrace();
    }
    if (str == null)
    {
        return false;
    }
    int sz = str.length();
    for (int i = 0; i < sz; i++)
    {
        if (isAsciiPrintable(str.charAt(i)) == false)
        {
        return false;
        }
    }
    return true;
    }

    /**
     * Used by isAsciiPrintable(Object obj)
     * 
     * @param ch
     * @return
     */
    public static boolean isAsciiPrintable(char ch)
    {
    return ch >= 32 && ch < 127;
    }

    /**
     * Does a simple search on the LDAP Directory
     * 
     * String ldapHostName = args[0]; String ldapPort = args[1]; String bindDn = args[2]; String bindDnPwd = args[3]; String searchBase = args[4]; // String searchScope=args[4]; String searchAttribute = args[5];
     * String searchAttributeValue = args[6];
     * 
     * @param args
     * 
     */
    public static void main(String[] args)
    {
    if (args.length == 7)
    {
        BasicAdminSearchBind basicjndisearch = new BasicAdminSearchBind(args);
    }
    else
    {
        System.out.println("\nYou must provide ldapHostName, ldapPort, bindDn, bindDnPwd, searchBase, searchAttribute and searchAttributeValue on the command line!\n");
    }
    }
}