golang实现LDAP服务器操作与自定义处理插件库gldap的使用

golang实现LDAP服务器操作与自定义处理插件库gldap的使用

概述

gldap是一个用于构建LDAP服务的框架,它定义了以下抽象组件:

  • Server: 支持LDAP和LDAPS(TLS)协议以及StartTLS请求
  • Request: 表示LDAP请求(bind、search、extended等)以及入站请求消息
  • ResponseWriter: 允许您编写请求响应
  • Mux: LDAP请求多路复用器,将入站请求与已注册的路由处理程序匹配
  • HandlerFunc: 提供给Mux的处理程序,用于处理单个LDAP请求

示例代码

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
	"strings"

	"github.com/jimlambrt/gldap"
)

func main() {
	// 创建新服务器
	s, err := gldap.NewServer()
	if err != nil {
		log.Fatalf("unable to create server: %s", err.Error())
	}

	// 创建路由器并添加bind处理程序
	r, err := gldap.NewMux()
	if err != nil {
		log.Fatalf("unable to create router: %s", err.Error())
	}
	r.Bind(bindHandler)
	r.Search(searchHandler)
	s.Router(r)
	go s.Run(":10389") // 监听10389端口

	// 当ctrl-c、sigint或sigterm发生时优雅地停止服务器
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()
	select {
	case <-ctx.Done():
		log.Printf("\nstopping directory")
		s.Stop()
	}
}

func bindHandler(w *gldap.ResponseWriter, r *gldap.Request) {
	resp := r.NewBindResponse(
		gldap.WithResponseCode(gldap.ResultInvalidCredentials),
	)
	defer func() {
		w.Write(resp)
	}()

	m, err := r.GetSimpleBindMessage()
	if err != nil {
		log.Printf("not a simple bind message: %s", err)
		return
	}

	if m.UserName == "alice" {
		resp.SetResultCode(gldap.ResultSuccess)
		log.Println("bind success")
		return
	}
}

func searchHandler(w *gldap.ResponseWriter, r *gldap.Request) {
	resp := r.NewSearchDoneResponse()
	defer func() {
		w.Write(resp)
	}()
	m, err := r.GetSearchMessage()
	if err != nil {
		log.Printf("not a search message: %s", err)
		return
	}
	log.Printf("search base dn: %s", m.BaseDN)
	log.Printf("search scope: %d", m.Scope)
	log.Printf("search filter: %s", m.Filter)

	if strings.Contains(m.Filter, "uid=alice") || m.BaseDN == "uid=alice,ou=people,cn=example,dc=org" {
		entry := r.NewSearchResponseEntry(
			"uid=alice,ou=people,cn=example,dc=org",
			gldap.WithAttributes(map[string][]string{
				"objectclass": {"top", "person", "organizationalPerson", "inetOrgPerson"},
				"uid":         {"alice"},
				"cn":          {"alice eve smith"},
				"givenname":   {"alice"},
				"sn":          {"smith"},
				"ou":          {"people"},
				"description": {"friend of Rivest, Shamir and Adleman"},
				"password":    {"{SSHA}U3waGJVC7MgXYc0YQe7xv7sSePuTP8zN"},
			}),
		)
		entry.AddAttribute("email", []string{"alice@example.org"})
		w.Write(entry)
		resp.SetResultCode(gldap.ResultSuccess)
	}
	if m.BaseDN == "ou=people,cn=example,dc=org" {
		entry := r.NewSearchResponseEntry(
			"ou=people,cn=example,dc=org",
			gldap.WithAttributes(map[string][]string{
				"objectclass": {"organizationalUnit"},
				"ou":          {"people"},
			}),
		)
		w.Write(entry)
		resp.SetResultCode(gldap.ResultSuccess)
	}
	return
}

当前支持的功能

  • ldapldapsmTLS连接
  • StartTLS请求
  • Bind请求
    • 简单认证(用户/密码)
  • Search请求
  • Modify请求
  • Add请求
  • Delete请求
  • Unbind请求

gldap.testdirectory

testdirectory包使用gldap构建,提供了一个内存中的测试LDAP服务,使得编写依赖LDAP服务的测试变得更加容易。

testdirectory也是一个很好的工作示例,展示了如何使用gldap构建自定义LDAP服务器以满足特定需求。

示例:

// 这个testdirectory示例展示了如何为单元测试启动一个测试目录,
// 测试完成后会自动停止
func TestExample(t *testing.T) {
	// 启动一个测试目录,在可用端口上运行ldaps(默认值),
	// 允许匿名绑定(默认覆盖)
	td := testdirectory.Start(t,
		testdirectory.WithDefaults(&testdirectory.Defaults{AllowAnonymousBind: true}),
	)
	// 创建一些新的测试用户条目(使用ou、密码等的默认值)
	users := testdirectory.NewUsers(t, []string{"alice", "bob"})
	// 设置测试目录的用户条目
	td.SetUsers(users...)

	// 在这里插入你的测试...
}

更多关于golang实现LDAP服务器操作与自定义处理插件库gldap的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现LDAP服务器操作与自定义处理插件库gldap的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang实现LDAP服务器操作与gldap库使用指南

LDAP服务器操作基础

LDAP(轻量级目录访问协议)是一种用于访问和维护分布式目录信息服务的协议。在Golang中,我们可以使用标准库和第三方库来操作LDAP服务器。

基本LDAP操作示例

package main

import (
	"fmt"
	"log"

	"github.com/go-ldap/ldap/v3"
)

func main() {
	// 连接到LDAP服务器
	conn, err := ldap.Dial("tcp", "ldap.example.com:389")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// 绑定(认证)
	err = conn.Bind("cn=admin,dc=example,dc=com", "password")
	if err != nil {
		log.Fatal(err)
	}

	// 搜索操作
	searchRequest := ldap.NewSearchRequest(
		"dc=example,dc=com", // 基础DN
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		"(objectClass=*)", // 过滤器
		[]string{"dn", "cn", "mail"}, // 返回的属性
		nil,
	)

	sr, err := conn.Search(searchRequest)
	if err != nil {
		log.Fatal(err)
	}

	// 处理搜索结果
	for _, entry := range sr.Entries {
		fmt.Printf("DN: %s\n", entry.DN)
		fmt.Printf("CN: %s\n", entry.GetAttributeValue("cn"))
		fmt.Printf("Mail: %s\n", entry.GetAttributeValue("mail"))
	}

	// 添加条目
	addRequest := ldap.NewAddRequest("uid=newuser,ou=people,dc=example,dc=com", nil)
	addRequest.Attribute("objectClass", []string{"top", "person", "organizationalPerson", "inetOrgPerson"})
	addRequest.Attribute("uid", []string{"newuser"})
	addRequest.Attribute("cn", []string{"New User"})
	addRequest.Attribute("sn", []string{"User"})
	addRequest.Attribute("mail", []string{"newuser@example.com"})

	err = conn.Add(addRequest)
	if err != nil {
		log.Fatal(err)
	}
}

gldap库介绍与使用

gldap是一个用于构建自定义LDAP服务器的Golang库,它允许你创建自己的LDAP服务器并实现自定义处理逻辑。

安装gldap

go get github.com/jimlambrt/gldap

基本gldap服务器示例

package main

import (
	"fmt"
	"log"
	"net"

	"github.com/jimlambrt/gldap"
)

func main() {
	// 创建LDAP服务器
	server := gldap.NewServer()
	
	// 添加自定义处理程序
	server.BindFunc("", handleBind)
	server.SearchFunc("", handleSearch)
	
	// 启动服务器
	listener, err := net.Listen("tcp", ":389")
	if err != nil {
		log.Fatal(err)
	}
	
	fmt.Println("LDAP server listening on :389")
	if err := server.Serve(listener); err != nil {
		log.Fatal(err)
	}
}

func handleBind(w gldap.ResponseWriter, m *gldap.Message) {
	r := m.GetBindRequest()
	res := gldap.NewBindResponse(gldap.ResultSuccess)
	
	// 简单的认证逻辑
	if string(r.Name()) == "cn=admin,dc=example,dc=com" && string(r.AuthenticationSimple()) == "password" {
		w.Write(res)
		return
	}
	
	res.SetResultCode(gldap.ResultInvalidCredentials)
	w.Write(res)
}

func handleSearch(w gldap.ResponseWriter, m *gldap.Message) {
	r := m.GetSearchRequest()
	
	// 简单的搜索处理
	if string(r.BaseObject()) == "dc=example,dc=com" {
		entry := gldap.NewSearchResultEntry("cn=admin,dc=example,dc=com")
		entry.AddAttribute("cn", "admin")
		entry.AddAttribute("mail", "admin@example.com")
		w.Write(entry)
	}
	
	w.Write(gldap.NewSearchResultDoneResponse(gldap.ResultSuccess))
}

高级gldap功能

1. 自定义后端存储

type MemoryBackend struct {
	entries map[string]map[string][]string
}

func NewMemoryBackend() *MemoryBackend {
	return &MemoryBackend{
		entries: make(map[string]map[string][]string),
	}
}

func (m *MemoryBackend) Add(dn string, attrs map[string][]string) error {
	m.entries[dn] = attrs
	return nil
}

func (m *MemoryBackend) Search(baseDN string, filter string) ([]map[string][]string, error) {
	var results []map[string][]string
	for dn, attrs := range m.entries {
		if strings.HasSuffix(dn, baseDN) {
			// 简单的过滤逻辑
			if filter == "" || strings.Contains(dn, filter) {
				results = append(results, attrs)
			}
		}
	}
	return results, nil
}

2. 使用自定义后端

func main() {
	backend := NewMemoryBackend()
	backend.Add("cn=admin,dc=example,dc=com", map[string][]string{
		"objectClass": {"top", "person"},
		"cn":         {"admin"},
		"mail":       {"admin@example.com"},
	})

	server := gldap.NewServer()
	server.BindFunc("", func(w gldap.ResponseWriter, m *gldap.Message) {
		// 认证逻辑
	})
	
	server.SearchFunc("", func(w gldap.ResponseWriter, m *gldap.Message) {
		r := m.GetSearchRequest()
		results, _ := backend.Search(string(r.BaseObject()), "")
		
		for _, attrs := range results {
			entry := gldap.NewSearchResultEntry(attrs["dn"][0])
			for attr, values := range attrs {
				if attr != "dn" {
					entry.AddAttribute(attr, values...)
				}
			}
			w.Write(entry)
		}
		w.Write(gldap.NewSearchResultDoneResponse(gldap.ResultSuccess))
	})
	
	// 启动服务器...
}

实际应用建议

  1. 安全性: 在生产环境中使用TLS加密连接
  2. 性能: 对于大型目录考虑使用缓存
  3. 日志: 记录所有操作以便审计
  4. 访问控制: 实现细粒度的访问控制

通过gldap库,你可以灵活地构建符合特定需求的LDAP服务器,无论是用于测试、模拟还是生产环境。

回到顶部