问题描述
我正在尝试使用 Spring LDAP 来检索和修改 Active Directory 服务器中的用户信息,但我无法通过 dn 检索用户记录以便我可以对其进行修改。
我可以使用 LdapTemplate.search
方法按用户名查找记录。记录中没有 dn
属性,但 distinguishedname
看起来应该是正确的。然而,当我使用 LdapTemplate.lookupContext
通过 dn 检索记录时,服务器说它无法通过它刚刚给我的 dn
找到记录。我做错了什么?
LdapTemplate
搜索方法没有为您提供无需从 Active Directory 进行第二次查询即可使用的句柄,这似乎是错误的。有没有更好的方法来做到这一点?
我创建了一个示例 Groovy 应用程序来演示该问题。我的 Spring Boot 应用程序创建了这个类,然后调用了 runTest 方法。
package edu.sunyjcc.gateway.ldap;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.ldap.LdapName;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import org.springframework.ldap.core.DirContextOperations;
public class ActiveDirectoryDNSample {
LdapTemplate ldapTemplate;
/** Attributes to fetch from server */
static attributeList = [
"sAMAccountName","distinguishedname","basedn","userPrincipalName",];
/** This will represent the record retrieved from Active Directory */
class Person {
/** Raw data from server */
Map attributes = [:];
/** Return the distinguished name */
String getDn() {
attributes?.distinguishedname;
}
String getUsername() {
attributes?.sAMAccountName;
}
String toString() {
"${this.username} <${this.getDn()}>"
}
/** Get a handle to the object from AD so we can modify it. This fails. */
def getContext() {
assert ldapTemplate;
println "in getContext()";
def dn = new LdapName(this.getDn());
println "...dn=$dn"
assert dn;
// The next line throws an exception.
DirContextOperations context = ldapTemplate.lookupContext(dn);
println "...context=$context"
}
}
/** Convert the attributes from AD into a Person object */
class RecordMapper implements AttributesMapper<Person> {
/** Create a Person object from the attribute map */
Person mapFromAttributes(Attributes attributes)
throws NamingException {
assert ldapTemplate;
Person prec = new Person(
ldapTemplate: ldapTemplate
);
attributeList.collect {
[attrName: it,attr: attributes.get(it)]
}.grep {it.attr}.each {
prec.attributes."${it.attrName}" = it.attr.get() as String;
}
return prec;
}
}
/** Get a user from Active Directory */
public List<Person> getByUsername(String username) throws Exception {
assert ldapTemplate;
AttributesMapper attrMapper = new RecordMapper();
assert attrMapper;
List s = ldapTemplate.search(
query().
where("sAMAccountName").is(username),attrMapper
);
if (s == null) {
System.err.println("s is null");
}
return s?:[];
}
/** Try to fetch a record and get a modify context for it */
public runTest(String username) {
println "In ActiveDirectoryDNSample.runText($username)"
assert ldapTemplate;
def records = getByUsername(username);
println "Retrieved ${records?.size()} records";
records.each {println " $it"}
println "Now try to get the context for the records"
records.each {
person ->
println " getting context for $person";
def context = person.getContext();
println " context=$context"
}
}
public ActiveDirectoryDNSample(LdapTemplate ldapTemplate ) {
this.ldapTemplate = ldapTemplate;
}
}
In ActiveDirectoryDNSample.runText(testuser)
Retrieved 1 records
testuser <CN=Test User,CN=Users,DC=jccadmin,DC=sunyjcc,DC=edu>
Now try to get the context for the records
getting context for testuser <CN=Test User,DC=edu>
in getContext()
...dn=CN=Test User,DC=edu
然后它以带有以下数据的 javax.naming.NameNotFoundException
结束。
[LDAP: error code 32 - 0000208D: NameErr: DSID-03100238,problem 2001 (NO_OBJECT),data 0,best match of:
'CN=Users,DC=edu'
\0]
感谢您给我的任何帮助。
解决方法
事实证明,确实有更好的方法。不是在搜索中使用 org.springframework.ldap.core.AttributesMapper
,而是使用 org.springframework.ldap.core.ContextMapper
。
在我的示例中,我向 Person 类添加了一个字段,该字段将保存对上下文的引用。
DirContextOperations context;
然后我创建了一个扩展 org.springframework.ldap.core.support.AbstractContextMapper
的新类。
class PersonContextMapper extends AbstractContextMapper {
@Override
protected Object doMapFromContext(DirContextOperations ctx) {
AttributesMapper attrMapper = new RecordMapper();
Person p = attrMapper.mapFromAttributes(ctx.attributes);
p.context = ctx;
return p;
}
}
当我将它传递给代替 ldapTemplate.search
的 AttributeMapper
方法时,我能够使用上下文来更新 Active Directory。