在LDAP服务器中验证用户

时间:2014-01-11 16:32:30

标签: 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,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;
    }
}

当我运行代码时,我得到了这个结果:

cn: rcbandit
gidNumber: 501
givenName: rcbandit
homeDirectory: /home/users/rcbandit
objectClass: inetOrgPerson, posixAccount, top
objectClass: inetOrgPerson, posixAccount, top
objectClass: inetOrgPerson, posixAccount, top
sn: rcbandit
uid: rcbandit
uidNumber: 1000
null

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

2 个答案:

答案 0 :(得分:1)

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

  

Bind操作的功能是允许在客户端和服务器之间交换认证信息。绑定操作应该被认为是&#34;验证&#34;操作

更多信息here

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

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

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) {
...
}

答案 1 :(得分:1)

如果您不使用JNDI,这很容易做到。所有Java LDAP SDK都允许方法通过同一LDAP连接进行绑定。我强烈建议您使用现代的当前LDAP SDK而不是使用JNDI。目前我们喜欢https://www.unboundid.com/products/ldap-sdk/

为什么要连接? - 无需在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");
    }
    }
}

-Jim