如何解决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
相关产品推荐
相关产品推荐

