LDAP搜索非常缓慢。

4

我正在使用JNDI连接LDAP活动目录,希望搜索名称包含搜索字符串的用户,因此我的搜索方法如下:

public static List<LDAPUser> searchContactsByName(
        ExtendedDirContext extendedDirContext, String name) {

    try {

        LdapContext ldapContext = extendedDirContext.getLdapContext();
        String searchBaseStr = extendedDirContext.getSearchBase();

        String sortKey = LDAPAttributes.NAME;
        ldapContext.setRequestControls(new Control[] { new SortControl(
                sortKey, Control.CRITICAL) });

        SearchControls searchCtls = new SearchControls();
        searchCtls.setTimeLimit(1000 * 10);

        String returnedAtts[] = { LDAPAttributes.USER_NAME,
                LDAPAttributes.NAME };
        searchCtls.setReturningAttributes(returnedAtts);

        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        String searchFilter = "(&(ObjectCategory=person)(cn=*" + name
                + "*))";

        NamingEnumeration<SearchResult> results = ldapContext.search(
                searchBaseStr, searchFilter, searchCtls);

        List<LDAPUser> users = new ArrayList<LDAPUser>(0);
        while (results.hasMoreElements()) {
            SearchResult sr = (SearchResult) results.next();
            Attributes attrs = sr.getAttributes();
            LDAPUser user = new LDAPUser();
            user.setName(attrs.get(LDAPAttributes.NAME).toString()
                    .replace("cn: ", ""));
            user.setUserName(attrs.get(LDAPAttributes.USER_NAME).toString()
                    .replace("sAMAccountName: ", ""));
            users.add(user);
        }

        return users;

    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

以下是我如何连接LDAP的方法:

public static ExtendedDirContext connectToLdap(MessageSource messageSource) {

    try {
        log.debug("connectToLdap");
        String providerUrl = messageSource.getMessage("provider.url", null,
                null);
        String securityPrincipal = messageSource.getMessage(
                "security.principal", null, null);
        String securityCredentials = messageSource.getMessage(
                "security.credentials", null, null);
        String searchBase = messageSource.getMessage("search.base", null,
                null);
        boolean ssl = Boolean.parseBoolean(messageSource.getMessage("ssl",
                null, null));
        LdapContext ldapContext;

        Hashtable<String, String> ldapEnv = new Hashtable<String, String>(
                11);
        ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        ldapEnv.put(Context.PROVIDER_URL, providerUrl);
        ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
        ldapEnv.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
        ldapEnv.put(Context.SECURITY_CREDENTIALS, securityCredentials);
        if (ssl)
            ldapEnv.put(Context.SECURITY_PROTOCOL, "ssl");
        // To get rid of the PartialResultException when using Active
        // Directory
        ldapEnv.put(Context.REFERRAL, "follow");
        ldapContext = new InitialLdapContext(ldapEnv, null);
        ExtendedDirContext extendedDirContext = new ExtendedDirContext();
        extendedDirContext.setLdapContext(ldapContext);
        extendedDirContext.setSearchBase(searchBase);
        log.debug("success connection to ldap");
        return extendedDirContext;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }

}

LDAP凭据如下:
provider.url=ldap://dc.fabrikam.com:389
security.principal=CN=administrator,CN=Users,DC=fabrikam,DC=com
security.credentials=password
search.base=dc=fabrikam,dc=com

为什么搜索需要花费如此长的时间来检索数据?我能做些什么改变来使搜索更快吗?因为我的AD中只有285个联系人。

1
你正在执行前缀通配符搜索,你期望什么?这将在O(n)时间内运行。 - Michael-O
@Michael-O,但我在AD中没有太多用户,只有285个。 - Mahmoud Saleh
忽略第一个星号并查看是否有变化。 - Michael-O
5
我发现将ldapEnv.put(Context.REFERRAL, "follow");更改为ldapEnv.put(Context.REFERRAL, "ignore");可以使搜索速度更快。 - Anya Shenanigans
如果省略 SortControl,查询是否运行得足够快?我怀疑通配符搜索(请注意,使用以 * 为前缀的搜索将忽略所有索引)和排序的组合。请参阅此文档,了解微软关于使用排序控件的建议。 - ig0774
显示剩余4条评论
3个回答

13

解决方法是将 ldapEnv.put(Context.REFERRAL, "follow"); 修改为 ldapEnv.put(Context.REFERRAL, "ignore");


是的,那也是我的解释。很奇怪!谢谢。 - Nicholas DiPiazza

3

你的筛选器:

"(&(ObjectCategory=person)(cn=*" + name + "*))"

可能是一个问题。

我建议您下载一个已知的LDAP工具(例如Apache Directory Studio Browser),并尝试不同的搜索过滤器,直到找到有效的过滤器。

首先,请尝试:

"(&(ObjectCategory=person)(cn= + name ))"

2

您说得对,

ldapEnv.put(Context.REFERRAL, "ignore") 

没有收到连接超时的异常。但是当我第一次尝试时,我遇到了部分异常。在我将LDAP配置端口从389更改为3268之后,我没有收到任何异常,成功构建了项目。3268端口是LDAP全局目录的端口。例如,Outlook客户端查询全局目录以查找地址簿信息。如果您遇到转介类型设置异常,可以尝试使用全局目录。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接