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

如何解决Golang中LDAP响应包读取错误:eoc child not allowed...

从Active Directory获取员工属性时遇到LDAP数据包解析错误

问题描述

尝试从Active Directory中获取员工属性(名字、姓氏、邮箱ID等)时,遇到错误:unable to read LDAP response packet: eoc child not allowed with definite length。已通过Apache Directory Studio连接AD实例,确认entry.GetAttributeValue()使用的属性名称正确。

相关代码

LDAP操作代码

package ldap

import (
    "fmt"
    "log/slog"

    "example.com/myapp/internal/datamodel"
    "example.com/myapp/internal/logging"
    "github.com/go-ldap/ldap/v3"
)

const (
    BindUsername = "CN=s-someuser    (Company),OU=Accounts,OU=Users,DC=ad,DC=ent,DC=company,DC=com"
    BindPassword = "Password123"
    FQDN         = "ldap://hostname:389"
    BaseDN       = "DC=ad,DC=ent,DC=example,DC=com"
)

var logger = logging.GetLogger()

func bindAndSearchLDAPEntry(filter string) (*ldap.SearchResult, error) {
    ldapconn, err := ldap.DialURL(FQDN)
    if err != nil {
        logger.Error("Failed to connect to LDAP", slog.String("Error", err.Error()))
        return nil, err
    }
    defer ldapconn.Close()

    ldapconn.Start()
    err = ldapconn.Bind(BindUsername, BindPassword)
    if err != nil {
        logger.Error(err.Error())
        return nil, err
    }

    searchReq := ldap.NewSearchRequest(BaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{}, nil)

    result, err := ldapconn.Search(searchReq)
    if err != nil {
        logger.Error("Error while searching LDAP", slog.String("Filter", filter), slog.String("Error", err.Error()))
        return nil, err
    }
    return result, nil
}

func GetEmployeeDetailsFromActiveDirectory() {
    emp := new(datamodel.Employee)
    result, err := bindAndSearchLDAPEntry("(sAMAccountName=UserName)")
    if err != nil && len(result.Entries) == 1 {
        entry := result.Entries[0]
        emp.GlobalEmployeeNumber = entry.GetAttributeValue("employeeID")
        emp.Username = entry.GetAttributeValue("sAMAccountName")
        emp.FirstName = entry.GetAttributeValue("givenName")
        emp.LastName = entry.GetAttributeValue("sn")
        fmt.Printf("%v", emp)
    } else if err != nil && len(result.Entries) == 0 {
        logger.Error("Did not find any entries in LDAP", slog.String("Error", err.Error()))
    } else if err != nil {
        logger.Error("Found multiple entries in LDAP", slog.String("Error", err.Error()))
    }
}

数据模型代码

package datamodel

type Employee struct {
    FirstName            string `json:"firstname"`
    LastName             string `json:"lastname"`
    Username             string `json:"username"`
    EmailID              string `json:"emailid"`
    GlobalEmployeeNumber string `json:"globalemployeenumber"`
}

错误信息

time=2023-10-10T08:55:28.808Z level=ERROR source=C:/code/tutorials/myapp/internal/ldap/activedirectory.go:41 msg="Error while searching LDAP" Filter="(sAMAccountName=UserName)" Error="unable to read LDAP response packet: eoc child not allowed with definite length"
panic: send on closed channel [recovered]
        panic: close of closed channel

goroutine 9 [running]:
github.com/go-ldap/ldap/v3.(*Conn).processMessages.func1()
        C:/Users/UserName/go/pkg/mod/github.com/go-ldap/ldap/v3@v3.4.6/conn.go:499 +0x2e5
panic({0x62f840?, 0x70d4e0?})
        C:/apps/Go/src/runtime/panic.go:914 +0x21f
github.com/go-ldap/ldap/v3.(*Conn).processMessages(0xc000136480)
        C:/Users/UserName/go/pkg/mod/github.com/go-ldap/ldap/v3@v3.4.6/conn.go:505 +0xf2
created by github.com/go-ldap/ldap/v3.(*Conn).Start in goroutine 1
        C:/Users/UserName/go/pkg/mod/github.com/go-ldap/ldap/v3@v3.4.6/conn.go:268 +0x96
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x8 pc=0x5586e9]

goroutine 1 [running]:
example.com/myapp/internal/ldap.GetEmployeeDetailsFromActiveDirectory()
        C:/code/tutorials/myapp/internal/ldap/activedirectory.go:50 +0x49
main.main()
        C:/code/tutorials/myapp/cmd/apiserver/myapv.go:25 +0xf
exit status 2

问题分析与解决方案

1. 逻辑判断错误导致空指针panic

GetEmployeeDetailsFromActiveDirectory函数中,判断条件写反:当err != nil时result必然为nil,直接访问result.Entries会触发空指针异常。修正后的逻辑:

func GetEmployeeDetailsFromActiveDirectory() {
    emp := new(datamodel.Employee)
    result, err := bindAndSearchLDAPEntry("(sAMAccountName=UserName)")
    if err != nil {
        logger.Error("LDAP操作失败", slog.String("Error", err.Error()))
        return
    }
    switch len(result.Entries) {
    case 1:
        entry := result.Entries[0]
        emp.GlobalEmployeeNumber = entry.GetAttributeValue("employeeID")
        emp.Username = entry.GetAttributeValue("sAMAccountName")
        emp.FirstName = entry.GetAttributeValue("givenName")
        emp.LastName = entry.GetAttributeValue("sn")
        emp.EmailID = entry.GetAttributeValue("mail")
        fmt.Printf("%v", emp)
    case 0:
        logger.Error("LDAP中未找到匹配条目")
    default:
        logger.Error("LDAP中找到多个匹配条目")
    }
}

2. 错误调用异步启动方法

bindAndSearchLDAPEntry中调用了ldapconn.Start(),该方法用于异步操作场景,而你使用的是同步Search方法,会导致内部goroutine通道重复关闭,引发send on closed channel错误。直接删除ldapconn.Start()调用即可。

3. 搜索请求未指定属性

当前搜索请求的属性列表为[]string{},会返回所有属性,建议明确指定需要的属性以减少数据传输:

searchReq := ldap.NewSearchRequest(
    BaseDN,
    ldap.ScopeWholeSubtree,
    ldap.NeverDerefAliases,
    0,
    0,
    false,
    filter,
    []string{"employeeID", "sAMAccountName", "givenName", "sn", "mail"},
    nil,
)

4. BindDN格式问题

BindUsername中的CN部分存在多余空格:CN=s-someuser (Company),AD的DN对空格敏感,建议从Apache Directory Studio复制准确的Bind用户DN。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 04:50:03