我目前面临的问题是LDAP-Query传递给LDAP服务器而LDAP服务器不会传递结果。
查询:(&(objectCategory = user)(mail =tester@oop-expert.de))
无法找到给定的电子邮件。所以预计会有空结果。
在大多数环境配置中,此查询将完美传递,LDAP会立即返回空结果。
我将问题分解为可能与发送查询的网络或主机有关的问题。因此,如果从一个主机/网络和另一个主机/网络发送LDAP-Server将“饿死”我的LDAP客户端,那么查询将会很好,因此LDAP客户端会关闭连接以进行客户端超时。
另一方面:搜索存在的电子邮件将始终导致立即结果。与哪个主机/网络无关。
LDAP-Server是一个Active Directory。有几个域控制器提供LDAP服务,配置“循环”。每个ip或dns访问对此主题没有任何影响。
通过ssl保护通信。 (LDAPS)
在所有情况下都建立了连接。因此身份验证并将查询传递给LDAP就可以了。
授权不应成为问题。我在所有情况下使用相同的LDAP用户。
LDAP客户端始终是使用InitialContext
的JAVA实现。
private InitialDirContext createDirContext(String principal, String credentials) throws NamingException {
if (credentials == null || credentials.isEmpty()) {
throw new LDAPLoginException();
}
return new InitialDirContext(createEnvironment(principal, credentials));
}
private Hashtable<String, String> createEnvironment(String principal, String credentials) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, this.ldapUrl);
// To get rid of the PartialResultException when using Active Directory
env.put(Context.REFERRAL, "follow");
// Needed for the Bind (User Authorized to Query the LDAP server)
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
return env;
}
构建和执行查询的代码:
private LDAPUser getLDAPUserInfoByUniqueField(String attr, String value) {
DirContext serviceUserContext = null;
NamingEnumeration<SearchResult> results = null;
try {
String searchString = "(&(objectCategory=user)(" + attr + "=" + value + "))";
serviceUserContext = createDirContext(this.serviceUserPrincipal, serviceUserCredentials);
results = serviceUserContext.search("", searchString, createSearchControls()); // blocking...
return createLDAPUserInfo(results);
} catch (LDAPLoginException e) {
throw e;
} catch (Exception e) {
throw new LDAPFatalException(e);
} finally {
try {
if (results != null)
results.close();
} catch (NamingException e) {
}
try {
if (serviceUserContext != null)
serviceUserContext.close();
} catch (NamingException e) {
}
}
}
实用方法:
private LDAPUser createLDAPUserInfo(NamingEnumeration<SearchResult> results) throws NamingException {
LDAPUser ldapUserInfo = null;
if (results.hasMore()) { // blocking here
SearchResult result = (SearchResult) results.next();
String sAMAccountName = extractsAMAccountName(result);
String distinguishName = extractDistinguishName(result);
String department = extractDepartment(result);
String email = extractEmail(result);
ldapUserInfo = new LDAPUser(sAMAccountName, distinguishName, department, email);
}
return ldapUserInfo;
}
private String extractsAMAccountName(SearchResult result) throws NamingException {
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get("sAMAccountName");
return (String) attr.get();
}
例外:
de.oopexpert.business.ldap.LDAPFatalException: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:90)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByEmail(LDAPImpl.java:57)
Caused by: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:242)
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
at de.oopexpert.business.ldap.impl.LDAPImpl.createLDAPUserInfo(LDAPImpl.java:139)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:84)
任何提示?
答案 0 :(得分:1)
我们与四个人一起努力。我们发现,当“关注refferals”时,它与服务器端的“名称解析问题”有关。客户特定的环境配置固有地禁止我们阻止此行为。 所以我们想出了一个解决方法。
由于我们在活动目录域中运行,因此我们拥有全局编录服务器。对此的重要声明来自Microsoft“technet”:
全局编录是一个分布式数据存储库,其中包含多域Active Directory域服务(AD DS)林中每个域中每个对象的可搜索部分表示形式。全局编录存储在已指定为全局编录服务器的域控制器上,并通过多主机复制进行分发。定向到全局编录的搜索更快,因为它们不涉及对不同域控制器的引用。 (来自What Is the Global Catalog?)
“不涉及refferals”这一短语将我们带到了我的JNDI环境配置,我在其中设置了以下内容:
env.put(Context.REFERRAL, "follow");
因此,当我这样做时,如果查询的结果似乎不完整,LDAP-Server将询问其他LDAP服务器。当我启动查询并在TCP级别上调试它时,我们的管理员确认了这一点。
不知何故,我可能没有真正正确地表示它,其他LDAP服务器的名称无法解决,导致客户端因为第一个LDAP服务器等待解决而导致饥饿。
我们尝试省略客户端参数“Context.Refferals = follow”。在这里我们立即得到回应。但回应并不像预期的那样:
javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name ''
这是因为第一个LDAP-Server有意见返回不完整的数据。
我们的管理员说,情况永远不会如此,因为“每个域控制器”都是“全局编录服务器”。
所以我的解决方法是:我将处理此异常并将其解释为“无结果”。