Golang对接Ldap(保姆级教程:概念搭建实战)

news2024/11/20 18:35:10

Golang对接Ldap(保姆级教程:概念&搭建&实战)

最近项目需要对接客户的LDAP服务,于是趁机好好了解了一下。LDAP实际是一个协议,对应的实现,大家可以理解为一个轻量级数据库。用户查询。比如:我要查询某个用户有没有对应的访问权限。

  • Windows的AD域就是LDAP的一个具体实现,当然AD域除了实现LDAP还实现了其他协议。

🚄本文教程所用代码地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/ldap_demo

0 Ldap(Light Directory Access Portocol)

我们日常的办公系统是不是有多个?每个系统之间是不是都有独立的账号密码?密码多了,有时候半天想不起来哪个密码对应哪个系统?

  • 如今大家再也不用为上面的的问题头疼了,因为“LDAP统一认证服务”已经帮助大家解决这些问题了

1 LDAP(轻量级目录访问协议,查询快,特殊的数据库)

LDAP(Light Directory Access Portocol),它是基于X.500标准的轻量级目录访问协议。

  • 是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。
  • 目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。

LDAP目录服务是由目录数据库和一套访问协议组成的系统。

2 LDAP主流厂商

细心的朋友应该会主要到,LDAP的中文全称是:轻量级目录访问协议,说到底LDAP仅仅是一个访问协议,那么我们的数据究竟存储在哪里呢?
在这里插入图片描述

3 核心概念&术语

核心概念:

  1. 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。
  2. 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。
  3. 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。
  4. 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。
①dc(Domain Component):域名部分,dc=com

域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com(一条记录的所属位置)

②uid(User Id):用户Id,uid=ziyi.zhou

用户ID ziyi.zhou(一条记录的ID)

③ou(Organization Unit):组织,ou=develop

组织单位,组织单位可以包含其他各种对象(包括其他组织单元),如“oa组”(一条记录的所属组织)

④cn(Common Name):公共名称,cn=jack
⑤sn(Surname):姓,sn=周
⑥dn(Distinguished Name):类比URL。一条记录的唯一标识。uid=ziyi.zhou,ou=oa组,dc=example,dc=com。

“uid=ziyi.zhou,ou=oa组,dc=example,dc=com”,一条记录的位置(唯一)

  • 类比URL:唯一定位Ldap服务中的一条记录。
⑦rdn(Relative dn):类比文件系统相对路径

相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson”

汇总表

使用LDAP流程:

  1. 连接到LDAP服务器;
  2. 绑定到LDAP服务器;
  3. 在LDAP服务器上执行所需的任何操作;
  4. 释放LDAP服务器的连接;
    在这里插入图片描述

参考:https://www.cnblogs.com/wilburxu/p/9174353.html

1 Linux搭建Ldap

以Centos为例搭建openldap。

1.1 搭建Ldap服务(Server端)

1. 安装openldap
# 安装openldap
yum -y install openldap-servers openldap-clients 
# 对管理员密码进行加密
slappasswd -s 123456
#加密后的密码(后面需要用到): {SSHA}VHNPrmccIO/QRS1IOBdwp++K/FkIkFac
2. 新建Ldap配置文件
# 新建Ldap配置文件
vim /etc/openldap/schema/changes.ldif

changes.ldif:

# 修改域名
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
# 注意修改
olcSuffix: dc=yi,dc=com

# 修改管理员用户
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
# 注意修改修改管理员用户 (olcRootDN):将管理员账户从原来的cn=admin,dc=ldap,dc=com改为cn=admin,dc=yi,dc=com。
olcRootDN: cn=admin,dc=yi,dc=com

# 修改管理员密码
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
# 替换为 slappasswd 生成后的结果
olcRootPW: {SSHA}VHNPrmccIO/QRS1IOBdwp++K/FkIkFac

# 修改访问权限
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}
  to *
  by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
  by dn.base="cn=admin,dc=yi,dc=com" read
  by * none
3. 创建域和组织
# 创建配置文件
vim /etc/openldap/schema/basedomain.ldif

basedomain.ldif:

# 配置文件内容,dn、dc都改成自己的配置
# 下列目录:DC=redmond,DC=wa,DC=microsoft,DC=com      
# 如果我们类比文件系统的话,可被看作如下文件路径:   
# Com/Microsoft/Wa/Redmond  
# 例如:CN=test,OU=developer,DC=domainname,DC=com
# 在上面的代码中 cn=test 可能代表一个用户名,ou=developer 代表一个 active directory 中的组织单位。这句话的含义可能就是说明 test 这个对象处在domainname.com 域的 developer 组织单元中

dn: dc=yi,dc=com
dc: yi
objectClass: top
objectClass: domain

dn: ou=People,dc=yi,dc=com
ou: People
objectClass: top
objectClass: organizationalUnit

dn: ou=Group,dc=yi,dc=com
ou: Group
objectClass: top
objectClass: organizationalUnit

应用配置:

# 应用域和配置,回车之后输入未加密前的密码:123456
ldapadd -x -D cn=admin,dc=yi,dc=com -W -f  /etc/openldap/schema/basedomain.ldif

# 查看用户列表,观察是否创建成功
ldapsearch -x -b "ou=People,dc=yi,dc=com" | grep dn

通过配置文件新建一个ou

# 新建配置文件
vim testGroup.ldif

# 配置文件内容ou:指明为Test Group
dn: ou=Test,dc=yi,dc=com
ou: Test
objectClass: top
objectClass: organizationalUnit

# 应用配置(cn=admin,dc=yi,dc=com:admin账户)
# 回车后输入加密前的密码:123456
ldapadd -x -D "cn=admin,dc=yi,dc=com" -W -f testGroup.ldif

在这里插入图片描述

  • 执行命令前:
    在这里插入图片描述
  • 执行命令,应用配置文件新增一个ou
    在这里插入图片描述

1.2 docker搭建可视化工具

1. 安装docker环境
yum install -y yum-utils
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
yum install docker
systemctl start docker
2. 开放ldap端口(399)
# 开放ldap server389端口或关闭防火墙
firewall-cmd --zone=public --add-port=389/tcp --permanent
systemctl restart firewalld
3. docker搭建可视化Ldap管理工具
# 配置主机地址&不开启HTTPS(默认是开启)
docker run -d --privileged -p 10004:80 --name myphpldapadmin \
  --env PHPLDAPADMIN_HTTPS=false --env PHPLDAPADMIN_LDAP_HOSTS=10.16.64.147  \
  --detach osixia/phpldapadmin

在这里插入图片描述

4. 浏览器访问

浏览器访问http://ip:10004

账户:

  • dn:cn=admin,dc=yi,dc=com
  • 密码:123456(我们生成密钥之前的明文,slappasswd -s 123456)

登录:
在这里插入图片描述
在这里插入图片描述

1.3 其他客户端工具推荐

1. windows:ldapadmin

官网地址:http://www.ldapadmin.org/

页面效果:
在这里插入图片描述

2. mac:LDAP Browser For MAC

官网地址:https://ldapbrowsermac.com/

在这里插入图片描述

使用效果:
在这里插入图片描述

2 Go操作Ldap

  1. 连接到LDAP服务器并绑定到LDAP服务器;(一般以管理员用户绑定,权限更大)
  2. 在LDAP服务器上执行所需的任何操作;
  3. 释放LDAP服务器的连接;

2.1 连接并以相应角色绑定LDAP服务器

安装依赖:

// 安装go操作ldap库
go get "github.com/go-ldap/ldap/v3"
func loginBind(config *LdapConfig) (*ldap.Conn, error) {
	l, err := ldap.DialURL(ldapURL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))

	if err != nil {
		panic(err)
		return nil, err
	}
	_, err = l.SimpleBind(&ldap.SimpleBindRequest{
		Username: config.BindUserDn, //"cn=admin,dc=yi,dc=com"
		Password: config.BindUserPassword, //"123456"
	})
	if err != nil {
		fmt.Println("ldap password is error: ", ldap.LDAPResultInvalidCredentials)
		return nil, err
	}
	fmt.Println("bind success...")
	return l, nil
}

2.2 执行对应操作(add、select、del等)

1. add

以添加用户为例

func addUser(conn *ldap.Conn, user User) error {
	//添加用户
	addRequest := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=QA,dc=yi,dc=com", user.username), nil)
	addRequest.Attribute("objectClass", []string{"inetOrgPerson"})
	addRequest.Attribute("ou", []string{"QA Group"})
	addRequest.Attribute("cn", []string{"41234123"})
	addRequest.Attribute("sn", []string{"xx2"})
	addRequest.Attribute("uid", []string{"10001"})
	addRequest.Attribute("userPassword", []string{user.password})
	err := conn.Add(addRequest)
	if err != nil {
		fmt.Println("add user error: ", err)
		return err
	}
	return nil
}

执行添加前:
在这里插入图片描述
运行main,在ou=QA下添加一条记录:

package main

import (
	"crypto/tls"
	"fmt"
	"github.com/go-ldap/ldap/v3"
	"github.com/ziyifast/log"
)

// ldap:未加密
// ldaps:加密
var ldapURL = "ldap://10.100.xx.xxx"

type LdapConfig struct {
	Addr             string
	BindUserDn       string
	BindUserPassword string
	BaseDn           string
	LoginName        string
	ObjectClass      []string
}

type User struct {
	username    string
	password    string
	telephone   string
	emailSuffix string
	snUsername  string
	uid         string
	gid         string
}

func loginBind(config *LdapConfig) (*ldap.Conn, error) {
	l, err := ldap.DialURL(ldapURL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))

	if err != nil {
		panic(err)
		return nil, err
	}
	_, err = l.SimpleBind(&ldap.SimpleBindRequest{
		Username: config.BindUserDn,
		Password: config.BindUserPassword,
	})
	if err != nil {
		fmt.Println("ldap password is error: ", ldap.LDAPResultInvalidCredentials)
		return nil, err
	}
	fmt.Println("bind success...")
	return l, nil
}

// 创建用户
func addUser(conn *ldap.Conn, user User) error {
	//添加用户
	addRequest := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=QA,dc=yi,dc=com", user.username), nil)
	addRequest.Attribute("objectClass", []string{"inetOrgPerson"})
	addRequest.Attribute("ou", []string{"QA Group"})
	addRequest.Attribute("cn", []string{"41234123"})
	addRequest.Attribute("sn", []string{"xx2"})
	addRequest.Attribute("uid", []string{"10001"})
	addRequest.Attribute("userPassword", []string{user.password})
	err := conn.Add(addRequest)
	if err != nil {
		fmt.Println("add user error: ", err)
		return err
	}
	return nil
}
func main() {
	//Ldap Config(用于校验后续的操作,包括查询用户是否存在、添加、删除等)
	config := new(LdapConfig)
	config.Addr = "ldap://10.100.xx.xxx"
	config.BaseDn = "dc=yi,dc=com"
	config.BindUserDn = "cn=admin,dc=yi,dc=com"
	config.LoginName = "uid"
	config.BindUserPassword = "123456"
	config.ObjectClass = []string{"inetOrgPerson"}

	//与建立ldap服务建立连接(方便后续查询新增删除项)
	conn, err := loginBind(config)
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	TestAddUser(conn)
}

// TestAddUser 测试添加用户
func TestAddUser(conn *ldap.Conn) {
	//添加用户
	user := User{
		username: "wangmazi",
		password: "123456",
	}
	err := addUser(conn, user)
	if err != nil {
		panic(err)
	}
	fmt.Println("add success...")
}

在这里插入图片描述
效果:

添加成功

在这里插入图片描述

2. select
  • 拼接查询条件
    • 单个条件(cn=jack):查询cn为jack的资源
    • 多个条件(&(cn=wangmazi)(ou=QA)):查询cn为wangmazi并且ou为QA的资源
  • ldap.NewSearchRequest(fmt.Sprintf(“%s”, config.BaseDn)调用查询接口
// 查询用户
func findUser(conn *ldap.Conn, config *LdapConfig, user User) (*ldap.SearchResult, error) {
	//多个条件:(&(cn=wangmazi)(ou=QA))
	filter := fmt.Sprintf("(cn=%s)", ldap.EscapeFilter(user.username))
	request := ldap.NewSearchRequest(fmt.Sprintf("%s", config.BaseDn),
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,
		0,
		false,
		filter,
		[]string{"userPassword"},
		nil,
	)
	searchResult, err := conn.Search(request)
	if err != nil {
		fmt.Println("search user error: ", err)
		return nil, err
	}
	return searchResult, nil
}

运行:

package main

import (
	"crypto/tls"
	"fmt"
	"github.com/go-ldap/ldap/v3"
	"github.com/ziyifast/log"
)

// ldap:未加密
// ldaps:加密
var ldapURL = "ldap://10.100.xx.xxx"

type LdapConfig struct {
	Addr             string
	BindUserDn       string
	BindUserPassword string
	BaseDn           string
	LoginName        string
	ObjectClass      []string
}

type User struct {
	username    string
	password    string
	telephone   string
	emailSuffix string
	snUsername  string
	uid         string
	gid         string
}

func loginBind(config *LdapConfig) (*ldap.Conn, error) {
	l, err := ldap.DialURL(ldapURL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))

	if err != nil {
		panic(err)
		return nil, err
	}
	_, err = l.SimpleBind(&ldap.SimpleBindRequest{
		Username: config.BindUserDn,
		Password: config.BindUserPassword,
	})
	if err != nil {
		fmt.Println("ldap password is error: ", ldap.LDAPResultInvalidCredentials)
		return nil, err
	}
	fmt.Println("bind success...")
	return l, nil
}

// 查询用户
func findUser(conn *ldap.Conn, config *LdapConfig, user User) (*ldap.SearchResult, error) {
	//多个条件:(&(cn=wangmazi)(ou=QA))
	filter := fmt.Sprintf("(cn=%s)", ldap.EscapeFilter(user.username))
	request := ldap.NewSearchRequest(fmt.Sprintf("%s", config.BaseDn),
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,
		0,
		false,
		filter,
		[]string{"userPassword"},
		nil,
	)
	searchResult, err := conn.Search(request)
	if err != nil {
		fmt.Println("search user error: ", err)
		return nil, err
	}
	return searchResult, nil
}
func main() {
	//Ldap Config(用于校验后续的操作,包括查询用户是否存在、添加、删除等)
	config := new(LdapConfig)
	config.Addr = "ldap://10.100.xx.xxx"
	config.BaseDn = "dc=yi,dc=com"
	config.BindUserDn = "cn=admin,dc=yi,dc=com"
	config.LoginName = "uid"
	config.BindUserPassword = "123456"
	config.ObjectClass = []string{"inetOrgPerson"}

	//与建立ldap服务建立连接(方便后续查询新增删除项)
	conn, err := loginBind(config)
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	TestFindUser(conn, config)
}


// TestFindUser 测试查询用户
func TestFindUser(conn *ldap.Conn, config *LdapConfig) {
	user := &User{
		username: "wangmazi",
	}
	searchResult, err := findUser(conn, config, *user)
	if err != nil {
		panic(err)
	}
	for _, entry := range searchResult.Entries {
		fmt.Println("find user: ", entry.DN)
		for _, v := range entry.Attributes {
			fmt.Println(v.Name, v.Values)
		}
	}
	return
}

效果:
在这里插入图片描述

3. del
  • 拼接要删除的DN(唯一标识,定位一个资源的具体位置)
  • ldap.NewDelRequest(dn, nil)调用删除请求
// 删除用户
func deleteUser(conn *ldap.Conn, config *LdapConfig, user User) error {
	dn := fmt.Sprintf("cn=%s,ou=QA,%s", user.username, config.BaseDn)
	log.Infof("del dn %v", dn)
	delRequest := ldap.NewDelRequest(dn, nil)
	err := conn.Del(delRequest)
	if err != nil {
		fmt.Printf("Failed to delete user %s: %v\n", dn, err)
		return err
	}
	fmt.Printf("User %s successfully deleted.\n", dn)
	return nil
}

删除前:
在这里插入图片描述
运行TestDeleteUser删除该记录:

package main

import (
	"crypto/tls"
	"fmt"
	"github.com/go-ldap/ldap/v3"
	"github.com/ziyifast/log"
)

// ldap:未加密
// ldaps:加密
var ldapURL = "ldap://10.100.xx.xxx"

type LdapConfig struct {
	Addr             string
	BindUserDn       string
	BindUserPassword string
	BaseDn           string
	LoginName        string
	ObjectClass      []string
}

type User struct {
	username    string
	password    string
	telephone   string
	emailSuffix string
	snUsername  string
	uid         string
	gid         string
}

func loginBind(config *LdapConfig) (*ldap.Conn, error) {
	l, err := ldap.DialURL(ldapURL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))

	if err != nil {
		panic(err)
		return nil, err
	}
	_, err = l.SimpleBind(&ldap.SimpleBindRequest{
		Username: config.BindUserDn,
		Password: config.BindUserPassword,
	})
	if err != nil {
		fmt.Println("ldap password is error: ", ldap.LDAPResultInvalidCredentials)
		return nil, err
	}
	fmt.Println("bind success...")
	return l, nil
}


// 查询用户
func findUser(conn *ldap.Conn, config *LdapConfig, user User) (*ldap.SearchResult, error) {
	//多个条件:(&(cn=wangmazi)(ou=QA))
	filter := fmt.Sprintf("(cn=%s)", ldap.EscapeFilter(user.username))
	request := ldap.NewSearchRequest(fmt.Sprintf("%s", config.BaseDn),
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,
		0,
		false,
		filter,
		[]string{"userPassword"},
		nil,
	)
	searchResult, err := conn.Search(request)
	if err != nil {
		fmt.Println("search user error: ", err)
		return nil, err
	}
	return searchResult, nil
}

func main() {
	//Ldap Config(用于校验后续的操作,包括查询用户是否存在、添加、删除等)
	config := new(LdapConfig)
	config.Addr = "ldap://10.100.xx.xxx"
	config.BaseDn = "dc=yi,dc=com"
	config.BindUserDn = "cn=admin,dc=yi,dc=com"
	config.LoginName = "uid"
	config.BindUserPassword = "123456"
	config.ObjectClass = []string{"inetOrgPerson"}

	//与建立ldap服务建立连接(方便后续查询新增删除项)
	conn, err := loginBind(config)
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	TestDeleteUser(conn, config)
}

运行后效果:
在这里插入图片描述
在这里插入图片描述

2.3 释放连接

func main() {
	//与建立ldap服务建立连接(方便后续查询新增删除项)
	conn, err := loginBind(config)
	if err != nil {
		panic(err)
	}
	err = conn.Close()
	if err != nil {
		panic(err)
	}
}

全部代码

代码地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/ldap_demo

package main

import (
	"crypto/tls"
	"fmt"
	"github.com/go-ldap/ldap/v3"
	"github.com/ziyifast/log"
)

// ldap:未加密
// ldaps:加密
var ldapURL = "ldap://10.16.xx.xx"

type LdapConfig struct {
	Addr             string
	BindUserDn       string
	BindUserPassword string
	BaseDn           string
	LoginName        string
	ObjectClass      []string
}

type User struct {
	username    string
	password    string
	telephone   string
	emailSuffix string
	snUsername  string
	uid         string
	gid         string
}

func loginBind(config *LdapConfig) (*ldap.Conn, error) {
	l, err := ldap.DialURL(ldapURL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true}))

	if err != nil {
		panic(err)
		return nil, err
	}
	_, err = l.SimpleBind(&ldap.SimpleBindRequest{
		Username: config.BindUserDn,
		Password: config.BindUserPassword,
	})
	if err != nil {
		fmt.Println("ldap password is error: ", ldap.LDAPResultInvalidCredentials)
		return nil, err
	}
	fmt.Println("bind success...")
	return l, nil
}

// 创建用户
func addUser(conn *ldap.Conn, user User) error {
	//添加用户
	addRequest := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=QA,dc=yi,dc=com", user.username), nil)
	addRequest.Attribute("objectClass", []string{"inetOrgPerson"})
	addRequest.Attribute("ou", []string{"QA Group"})
	addRequest.Attribute("cn", []string{"41234123"})
	addRequest.Attribute("sn", []string{"xx2"})
	addRequest.Attribute("uid", []string{"10001"})
	addRequest.Attribute("userPassword", []string{user.password})
	err := conn.Add(addRequest)
	if err != nil {
		fmt.Println("add user error: ", err)
		return err
	}
	return nil
}

// 查询用户
func findUser(conn *ldap.Conn, config *LdapConfig, user User) (*ldap.SearchResult, error) {
	//多个条件:(&(cn=wangmazi)(ou=QA))
	filter := fmt.Sprintf("(cn=%s)", ldap.EscapeFilter(user.username))
	request := ldap.NewSearchRequest(fmt.Sprintf("%s", config.BaseDn),
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,
		0,
		false,
		filter,
		[]string{"userPassword"},
		nil,
	)
	searchResult, err := conn.Search(request)
	if err != nil {
		fmt.Println("search user error: ", err)
		return nil, err
	}
	return searchResult, nil
}

// 删除用户
func deleteUser(conn *ldap.Conn, config *LdapConfig, user User) error {
	dn := fmt.Sprintf("cn=%s,ou=QA,%s", user.username, config.BaseDn)
	log.Infof("del dn %v", dn)
	delRequest := ldap.NewDelRequest(dn, nil)
	err := conn.Del(delRequest)
	if err != nil {
		fmt.Printf("Failed to delete user %s: %v\n", dn, err)
		return err
	}
	fmt.Printf("User %s successfully deleted.\n", dn)
	return nil
}

func main() {
	//Ldap Config(用于校验后续的操作,包括查询用户是否存在、添加、删除等)
	config := new(LdapConfig)
	config.Addr = "ldap://10.16.xx.xx"
	config.BaseDn = "dc=yi,dc=com"
	config.BindUserDn = "cn=admin,dc=yi,dc=com"
	config.LoginName = "uid"
	config.BindUserPassword = "123456"
	//客户不配置username,我们需要根据配置的ObjectClass查询出对应的用户。
	//因为如果用户配置的是cn,那么可能会查询出一些组织、其他设备等,所以为了将Ldap第三方用户纳管过来,我们需要添加ObjectClass
	config.ObjectClass = []string{"inetOrgPerson"}

	//与建立ldap服务建立连接(方便后续查询新增删除项)
	conn, err := loginBind(config)
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	TestDeleteUser(conn, config)
}

// TestAddUser 测试添加用户
func TestAddUser(conn *ldap.Conn) {
	//添加用户
	user := User{
		username: "wangmazi",
		password: "123456",
	}
	err := addUser(conn, user)
	if err != nil {
		panic(err)
	}
	fmt.Println("add success...")
}

// TestFindUser 测试查询用户
func TestFindUser(conn *ldap.Conn, config *LdapConfig) {
	user := &User{
		username: "wangmazi",
	}
	searchResult, err := findUser(conn, config, *user)
	if err != nil {
		panic(err)
	}
	for _, entry := range searchResult.Entries {
		fmt.Println("find user: ", entry.DN)
		for _, v := range entry.Attributes {
			fmt.Println(v.Name, v.Values)
		}
	}
	return
}

func TestDeleteUser(conn *ldap.Conn, config *LdapConfig) {
	user := User{
		username: "wangmazi",
	}
	err := deleteUser(conn, config, user)
	if err != nil {
		panic(err)
	}

}

3 项目对接思路

项目登录对接:支持LDAP登录,用户可直接输入LDAP服务端存在的用户,直接登录系统

  1. 页面提供入口配置LDAP服务
    • Addr:LDAP服务端地址
    • BindUserDn:LDAP管理员用户dn
    • BindUserPassword:LDAP管理员密码
    • BaseDn:操作范围(dc=yi,dc=com表明操作这个范围下的数据)
    • LoginName:配置以哪个参数登录
  2. 页面输入LDAP对应账号
  3. 根据LDAP配置连接LDAP服务端,查询用户输入的账号是否存在,密码是否正确
  4. 可以直接纳管LDAP用户到我方系统,建立对应关系。比如:用户审计…
type LdapConfig struct {
	Addr             string
	BindUserDn       string
	BindUserPassword string
	BaseDn           string
	LoginName        string
	ObjectClass      []string
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1624083.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高频前端面试题汇总之Vue篇

1. Vue的基本原理 当一个Vue实例创建时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0使用proxy )将它们转为 getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实…

【ruoyi-vue】登录解析(后端)

调试登录接口 进入实现类可以有 验证码校验 登录前置校验 用户验证 验证码校验 通过uuid获取redis 中存储的验证码信息,获取后对用户填写的验证码数据进行校验比对 用户验证 1.进入控制器的 /login 方法 2.进入security账号鉴权功能,经过jar内的流…

算法-动态规划专题

文章目录 前言 : 动态规划简述1 . 斐波那契模型1.1 泰波那契数列1.2 最小花费爬楼梯1.3 解码方法 前言 : 动态规划简述 动态规划在当前我们的理解下,其实就是一种变相的递归,我们查看一些资料也可以知道,动态规划其实属于递归的一个分支,通过把递归问题开辟的栈帧通过一定的手…

代码随想录第45天|70. 爬楼梯 (进阶)322. 零钱兑换

70. 爬楼梯 &#xff08;进阶&#xff09; 57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; (kamacoder.com) 代码随想录 (programmercarl.com) 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶…

企业数字化转型,“业务”先行

在当今时代&#xff0c;数字化转型已经成为企业发展的必经之路。数字化转型&#xff0c;简而言之&#xff0c;就是运用数字技术&#xff0c;对企业运营管理的各个环节进行深度改造&#xff0c;以提升企业的运营效率和市场竞争力。据有关机构研究测算&#xff0c;数字化转型可使…

【静态分析】静态分析笔记07 - 指针分析基础

参考&#xff1a; 【课程笔记】南大软件分析课程7——指针分析基础&#xff08;课时9/10&#xff09; - 简书 -------------------------------------------------------------- 1. 指针分析规则 规则&#xff1a;采用推导形式&#xff0c;横线上面是条件&#xff0c;横线下…

Linux开发板配置静态IP

1、查看网口信息&#xff0c;易知eth0无IP地址 ifconfig2、首先分配一个IP地址 sudo ifconfig eth0 192.168.5.8 up3、此时配置的IP地址只是临时的&#xff0c;当你reboot重启板子上电后&#xff0c;ip地址会消失&#xff0c;因此需要为板子配置静态ip&#xff0c;避免每次上…

说方法不如传授经验向媒体投稿你可以这样

在信息爆炸的时代,作为单位的信息宣传员,肩负着将本单位的重要资讯、活动成果、政策解读等内容有效传播至公众视野的重任。其中,向各类媒体投稿无疑是实现这一目标的重要途径。然而,传统的邮件投稿方式常常让我深感力不从心,费时费力不说,成功率低、出稿慢等问题更是让我和领导…

SQL注入简单总结

一、SQL注入是什么 SQL注入即&#xff1a;是指web应用程序对用户输入数据的合法性没有判断或过滤不严&#xff0c;攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句&#xff0c;在管理员不知情的情况下实现非法操作&#xff0c;以此来实现欺骗数据库服…

【论文推导】基于有功阻尼的转速环PI参数整定分析

前言 在学习电机控制的路上&#xff0c;PMSM的PI电流控制是不可避免的算法之一&#xff0c;其核心在于内环电流环、外环转速环的设置&#xff0c;来保证转速可调且稳定&#xff0c;并且保证较好的动态性能。整个算法仿真在《现代永磁同步电机控制原理及matlab仿真》中已详细给出…

前端项目中使用插件prettier/jscodeshift/json-stringify-pretty-compact格式化代码或json数据

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、json代码格式化-选型二、json-stringify-pretty-compact简单试用三、prettier在前端使用四、查看prettier支持的语言和插件五、使用prettier格式化vue代码最终效果如图&#xff1a; ![在这里插入图片描述](https://im…

3.8设计模式——State 状态模式(行为型)

意图 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 结构 Context&#xff08;上下文&#xff09;定义客户感兴趣的接口&#xff1b;维护一个ConcreteState子类的实例&#xff0c;这个实例定义当前状态。State&#xff08;状态&#xff09;定义…

[x86] OpenBMC简介

什么是 OpenBMC&#xff1f; OpenBMC 被设计为一个完整的开源 Linux 发行版&#xff0c;可以灵活地进行定制以支持不同的 SoC 或主板。 传统的BMC由固件提供商专有构建&#xff0c;是闭源的&#xff0c;这意味着它不具有在开放平台上修改的灵活性。 另一方面&#xff0c;OpenB…

链栈的基本操作(c语言实现)

栈的概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 栈的结构 定义栈…

做抖音小店,“自然流量”和“达人带货”,选择哪个更香?

大家好&#xff0c;我是电商笨笨熊 做抖音小店&#xff0c;关于选择自然流还是达人带货&#xff0c;从推出时就一直争吵到现在&#xff1b; 有人觉得自然流不需要佣金&#xff0c;一次性带来的爆单量很大&#xff1b; 有人觉得达人带货细水长流&#xff0c;虽然需要佣金&…

Docker 网络与资源控制

一 Docker 网络实现原理 Docker使用Linux桥接&#xff0c;在宿主机虚拟一个Docker容器网桥(docker0)&#xff0c;Docker启动一个容器时会根 据Docker网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c;同时Docker网桥是每个容器的默 认网关。因为在同…

2000-2022年各区县农产品产量数据

2000-2022年县域农产品产量数据 1、时间&#xff1a;2000-2022年 2、指标&#xff1a;统计年度、县域名称、所属地级市、所属省份、地区编码ID、县域代码、产品种类或名称、单位、产量、 3、来源&#xff1a;统计局、县域统计年鉴、各区县政府官网 4、范围&#xff1a;具体…

基于Linux的Ncurse库的贪吃蛇项目

贪吃蛇项目的意义 承上启下&#xff1a;从C语言基础的学习&#xff1a;数据结构链表基础、C变量、流程控制、函数、指针、结构体等。过渡到Linux系统编程&#xff1a;文件编程、进程、线程、通信、第三方等。 Linux终端图形库curses curses的名字起源于"cursor optimiz…

算法学习笔记Day8——回溯算法

本文解决几个问题&#xff1a; 回溯算法是什么&#xff1f;解决回溯算法相关的问题有什么技巧&#xff1f;回溯算法代码是否有规律可循&#xff1f; 一、介绍 1.回溯算法是什么&#xff1f; 回溯算法就是个多叉树的遍历问题&#xff0c;关键在于在前序和后序时间点做一些操作…

大学生在线考试|基于SprinBoot+vue的在线试题库系统系统(源码+数据库+文档)

大学生在线考试目录 基于SprinBootvue的在线试题库系统系统 一、前言 二、系统设计 三、系统功能设计 试卷管理 试题管理 考试管理 错题本 考试记录 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#…