无法通过 Spring LDAP 获取 Active Directory 记录的修改上下文

问题描述

我正在尝试使用 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.searchAttributeMapper 方法时,我能够使用上下文来更新 Active Directory。