You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring LdapTemplate search()方法无法返回dn属性的问题

问题:Spring LDAP搜索结果中缺少DN属性

我编写了一个基于CN执行LDAP搜索并映射所有返回属性的Java程序,代码如下。程序运行后返回的属性列表里没有DN属性,但用ldapsearch执行等价搜索时,DN能正常返回。

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.query.ConditionCriteria;
import org.springframework.ldap.query.ContainerCriteria;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;

public class LDAPSearchTest {

    public static void main(String[] args) {
        LdapContextSource contextSource = new DefaultSpringSecurityContextSource("ldaps://localhost:636");
        contextSource.setUserDn("");
        contextSource.setPassword("");
        contextSource.setAnonymousReadOnly(false);
        contextSource.afterPropertiesSet();

        LdapTemplate ldapTemplate = new LdapTemplate(contextSource);

        ConditionCriteria condition
                = query()
                        .base("baseDNValue")
                        .where("cn");

        ContainerCriteria filter = condition.is("cnValue");
        List<Map<String, List<Object>>> results = ldapTemplate.search(
                filter,
                new DefaultAttributesMapper());
        for (Map<String, List<Object>> groupSearchResult : results) {
            for (String attributeName : groupSearchResult.keySet()) {
                System.out.println(String.format("	%s", attributeName));
                for (Object attributeValue : groupSearchResult.get(attributeName)) {
                    System.out.println(String.format("		%s", attributeValue == null ? null : attributeValue.toString()));
                }
            }
        }
    }
    private static class DefaultAttributesMapper implements AttributesMapper<Map<String, List<Object>>> {

        @Override
        public Map<String, List<Object>> mapFromAttributes(Attributes attributes) throws NamingException {
            Map<String, List<Object>> mappedAttributes = new LinkedHashMap<>();
            NamingEnumeration<? extends Attribute> attributesEnumeration = attributes.getAll();
            while (attributesEnumeration.hasMore()) {
                Attribute nextAttribute = attributesEnumeration.next();
                mappedAttributes.put(nextAttribute.getID(), new ArrayList<>());
                NamingEnumeration attributeValuesEnumeration = nextAttribute.getAll();
                while (attributeValuesEnumeration.hasMore()) {
                    Object nextAttributeValue = attributeValuesEnumeration.next();
                    mappedAttributes.get(nextAttribute.getID()).add(nextAttributeValue);
                }
            }
            return mappedAttributes;
        }
    }
}

原因分析

  1. DN的本质:LDAP中的DN(Distinguished Name)是条目的唯一路径标识,不属于条目本身的属性集合,它是用来定位条目的“地址”,而非条目存储的属性。
  2. 工具与API的差异:ldapsearch工具默认会将DN作为搜索结果的元数据返回,但Spring LDAP底层依赖的JDNI API中,Attributes对象仅包含条目自身存储的属性,不会自动包含DN。
  3. Mapper的局限性:当前使用的AttributesMapper仅负责处理条目属性,无法访问到DN这类元数据信息。

解决思路

方案1:改用ContextMapper获取DN和属性

ContextMapper可以访问到DirContextOperations对象,该对象同时包含DN和条目属性。修改搜索逻辑如下:

import java.util.Collections;
// 需导入Collections类

// 替换原search代码段
List<Map<String, List<Object>>> results = ldapTemplate.search(
        filter,
        new ContextMapper<Map<String, List<Object>>>() {
            @Override
            public Map<String, List<Object>> mapFromContext(Object ctx) throws NamingException {
                DirContextOperations dirContext = (DirContextOperations) ctx;
                Map<String, List<Object>> mappedAttributes = new LinkedHashMap<>();
                
                // 添加DN到结果Map
                mappedAttributes.put("dn", Collections.singletonList(dirContext.getDn().toString()));
                
                // 遍历并添加其他属性
                Attributes attributes = dirContext.getAttributes();
                NamingEnumeration<? extends Attribute> attrEnum = attributes.getAll();
                while (attrEnum.hasMore()) {
                    Attribute attr = attrEnum.next();
                    List<Object> values = new ArrayList<>();
                    NamingEnumeration<?> valueEnum = attr.getAll();
                    while (valueEnum.hasMore()) {
                        values.add(valueEnum.next());
                    }
                    mappedAttributes.put(attr.getID(), values);
                }
                return mappedAttributes;
            }
        });

方案2:直接获取SearchResult对象

SearchResult包含条目名称(可转换为DN)和属性,使用SearchResultCallbackHandler处理结果:

ldapTemplate.search(filter, new SearchResultCallbackHandler() {
    @Override
    public void handleSearchResult(SearchResult searchResult) throws NamingException {
        // 获取完整DN
        String dn = searchResult.getNameInNamespace();
        System.out.println("dn: " + dn);
        
        // 处理其他属性
        Attributes attributes = searchResult.getAttributes();
        NamingEnumeration<? extends Attribute> attrEnum = attributes.getAll();
        while (attrEnum.hasMore()) {
            Attribute attr = attrEnum.next();
            System.out.println("	" + attr.getID());
            NamingEnumeration<?> valueEnum = attr.getAll();
            while (valueEnum.hasMore()) {
                System.out.println("		" + valueEnum.next());
            }
        }
    }
});

内容的提问来源于stack exchange,提问作者alessandro ferrucci

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:13:10