我们工作在和其他组织配合时,我们可能不是作为服务器搭建的一方,而是属于客户端的一方,需要去获取服务器的组织结构,按照某些条件去获取服务器的数据,也可以是同步组织结构
虽然说 golang 的数据结构没有 c++ 那么丰富,不过对于这个 ldap 还是有相应的库来进行处理的
官方文档地址:https://pkg.go.dev/gopkg.in/ldap.v3
我们也可以下载 github 上面的这个库
go get github.com/go-ldap/ldap/v3
golang 对于 ldap 库最新的版本是 Version: v3.1.0
开始编码
我们来写一个 demo ,获取我们上次搭建的 ldap 服务器上的组织结构
这是我们简单搭建的 ldap 服务器,可以使用 LDAP Admin 可视化管理工具来查看具体的页面效果
我们把这个库下载下来后,我们的编码思路如下:
- 填写 ldap 服务器地址以及填写相应的管理员信息,与 ldap 服务器建立连接
- 编写查询请求,并开始向 ldap 服务器进行查询
- 将查询结构,按照 ldap v3 库提供的方式 打印出效果来
连接 服务器
我们可以使用 func DialURL(addr string, opts ...DialOpt) (*Conn, error)
函数来与 ldap 服务器建立连接
ml, err := ldap.DialURL("ldap://xxxx") if err != nil { log.Fatal(err) } defer ml.Close()
我们填入的地址中,可以不用输入端口号,库函数已经有给我们做好处理,我们可以来看看源码
DialURL
函数用于连接 ldap 服务器,连接成功会给我们返回一个新的连接
我们可以继续看一下这个函数调用 c, err := dc.dial(u)
golang 的库会根据我们填写的地址是 ldap 还是 ldaps 来判断是做加密传输还是不加密传输,与之对应的就是访问不加密的用 389 端口,加密的就使用 636 端口
添加管理员绑定信息
我们添加的 ldap 域信息为:dc=xiaomotong,dc=com
我的管理员是:cn=admin,dc=xiaomotong,dc=com
_, err = ml.SimpleBind(&ldap.SimpleBindRequest{ Username: "cn=admin,dc=xiaomotong,dc=com", Password: "123123", }) if err != nil { log.Fatalf("Failed to bind: %s\n", err) } fmt.Println("connect successfully !!")
来看看实际的 SimpleBindRequest
数据结构
// SimpleBindRequest represents a username/password bind operation type SimpleBindRequest struct { // Username is the name of the Directory object that the client wishes to bind as Username string // Password is the credentials to bind with Password string // Controls are optional controls to send with the bind request Controls []Control // AllowEmptyPassword sets whether the client allows binding with an empty password // (normally used for unauthenticated bind). AllowEmptyPassword bool }
- Username
- Password
客户端需要绑定的域用户和密码
- Controls
需要绑定请求的控件
- AllowEmptyPassword
是否允许空密码,若是空密码,一般是绑定一个未授权的用户
编写查询请求,并开始查询 ldap 服务器
searchRequest := ldap.NewSearchRequest( "dc=xiaomotong,dc=com", ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(ou=People)", []string{}, nil, ) searchResult, err := ml.Search(searchRequest) if err != nil { log.Println("can't search ", err.Error()) } log.Printf("%d", len(searchResult.Entries))
编写查询请求,也就是简单的给我们的结构体进行一个负值操作,填写好对相应的参数,即可开始查询,一起来看看这个结构体NewSearchRequest
基本上就是填写相应的域信息
- BaseDN , 一个域唯一的标识
- scope 范围的选择,我们默认选择
ScopeWholeSubtree
,查询所有的子树 - DerefAliases , SizeLimit,TimeLimit,TypesOnly 填写默认值即可
- Filter , 查询需要的过滤条件,可以按照我们的实际情况写条件,就像写查询数据库的条件一样,这里不能为空,否则会程序崩溃
F:\codegitee\golang_study\later_learning\ldap_test>go run main.go connect successfully !! 2021/11/06 21:07:59 can't search LDAP Result Code 201 "Filter Compile Error": ldap: error parsing filter panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x4 pc=0xa33cef] goroutine 1 [running]: main.main() F:/codegitee/golang_study/later_learning/ldap_test/main.go:40 +0x24f exit status 2
- Attributes , 需要返回的属性有哪些,是一个切片,如果我们默认填空,则会返回所有属性
查看对应的 Search
函数源码
代码的大致逻辑是,doRequest
将数据组包向 ldap 服务器发送请求,请求成功之后,将响应的数据按照 tag 不同的内容进行解析
最终返回一个 *SearchResult
查询结果的指针
输入查询信息
for _, item := range searchResult.Entries { item.Print() fmt.Printf("\n\n") }
上图源码我们可以看到输出查询信息就是遍历一下 searchResult.Entries
,我们可以来看看对应的数据结构
我们可以看到结果里面,有一个 Entries []*Entry
是一个切片,里面放了多个 *Entry
, 在 ldap 服务器中, 1 个 Entry 就代表一条唯一的记录
Entry 结构体就是对应的 DN,一条记录唯一的辨别名 , 和他涉及的属性
EntryAttribute 属性结构体中,我们可以看到 有 Name ,有 Values ,这里就是对应我们之前说到的 RDN,也就是一个键值对,多个键值对组成一个 DN
最终我们来查看一下效果
>go run main.go connect successfully !! 2021/11/06 21:20:58 1 DN: ou=People,dc=xiaomotong,dc=com objectClass: [organizationalUnit] ou: [People]
结果是输出了 1 条信息,没错,因为我们的 ou=people 只有 1条记录,如果我们需要查询整个 ldap服务器的所有数据,则我们可以将上述代码的 Filter 位置,修改成 objectClass=*
解释上述结果:
- DN 表示唯一的记录,是辨别名的意思
- objectClass 是一个类,这里对应的是 organizationalUnit ,表示组织单元OU,可以理解为 组
- ou: [People] 指的是这个 ou 对应的名字是 People
欢迎点赞,关注,收藏
朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力
好了,本次就到这里
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是阿兵云原生,欢迎点赞关注收藏,下次见~