【问题标题】:Authenticate user in LDAP server在 LDAP 服务器中验证用户
【发布时间】:2014-01-11 16:32:29
【问题描述】:

我用这个配置配置了 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

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

【问题讨论】:

    标签: java ldap openldap


    【解决方案1】:

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

    Bind 操作的作用是允许客户端和服务器之间交换身份验证信息。 Bind 操作应该被认为是“身份验证”操作。

    更多信息here

    您的代码中的绑定操作将在您创建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) {
    ...
    }
    

    【讨论】:

    • 有一个问题,我想使用一个连接来验证所有用户。您能否编辑我的代码如何修复它?
    • @PeterPenzov 这是一个有趣的要求,我想不出环境默认允许这样做的方式,除非您提供一些有关它们的细节。
    • 你的意思是我需要在 LDAP 服务器中配置一些额外的东西?
    【解决方案2】:

    如果您不使用 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");
        }
        }
    }
    

    -吉姆

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-20
      • 2016-05-03
      • 2014-04-11
      • 1970-01-01
      • 2013-08-20
      • 2014-10-14
      • 2020-04-15
      • 2015-06-22
      相关资源
      最近更新 更多