LDAP提供标准的访问协议,采用的是ASN.1协议格式,不是很好理解,不过应用层方面提供c/c++的api,可以很方便的使用
windows下可以使用微软提供的一系列接口,MSDN文档介绍可查看
库的使用
打开MSDN的文档,随便打开一个接口,在接口介绍底部会说明使用的库及头文件
使用时需包含头文件winldap.h
链接库Wldap32.lib
#pragma comment(lib,"Wldap32.lib")
Qt的也可以在pro文件添加
LIBS += -lWldap32
连接ldap-server
ldap是基于TCP/IP协议,实现的是一种可靠连接,连接方式和套接字连接相似
按以下代码方式进行连接即可,简单身份验证流程是固定的
PWSTR host = (PWSTR)L"192.168.33.167"; // 主机
ULONG port = LDAP_PORT; // 端口
ULONG version = LDAP_VERSION3; // 版本
// 认证信息
PWSTR dn = (PWSTR)L"cn=admin,c=cn"; // 管理员
PWSTR cred = (PWSTR)L"xxxxx"; // 密码
ULONG method = LDAP_AUTH_SIMPLE; // 识别方法
LDAP *ld = NULL; // 连接的句柄
ULONG rc = 0; // 返回值
// 初始化 LDAP
ld = ldap_init(host, port);
if (ld == NULL) {
fprintf(stderr, "ldap_init failed");
return -1;
}
printf("ldap_init success\n");
// 设置协议版本为 3.0(默认 2.0)
rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_set_option: rc: %d\n", rc);
return -1;
}
printf("ldap_set_option success\n");
// 连接 LDAP 服务器
rc = ldap_connect(ld, NULL);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_connect: rc: %d\n", rc);
return -1;
}
printf("ldap_connect success\n");
// 向 LDAP 服务器认证客户端,无身份验证访问dn和cred传NULL,只能进行查询访问,无法进行增删改
rc = ldap_bind_s(ld, dn, cred, method);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_bind_s: rc: %d\n", rc);
return -1;
}
printf("ldap_bind_s success\n");
对比使用客户端工具连接
新增
相关要点看代码中的注释
LDAPMod OClass, FName, cnName;
//新增的条目dn
PWSTR entry_dn = (PWSTR)L"cn=testCert,c=cn";
const wchar_t *cn_value[] = {L"testCert", NULL};
cnName.mod_op = LDAP_MOD_ADD;
//属性类型
cnName.mod_type = (PWSTR)L"cn";
cnName.mod_values = (PWSTR *)cn_value;
const wchar_t *oc_values[] = {L"inetOrgPerson", NULL};
OClass.mod_op = LDAP_MOD_ADD;
OClass.mod_type = (PWSTR)L"objectClass";
//mod_values为wchar_t **类型,定义的是wchar_t *的数组,数组末尾需追加NULL
OClass.mod_values = (PWSTR *)oc_values;
const wchar_t *gn_values[] = {L"testCert", NULL};
FName.mod_op = LDAP_MOD_ADD;
FName.mod_type = (PWSTR)L"sn";
FName.mod_values = (PWSTR *)gn_values;
//该数组末尾也要追加NULL
LDAPMod *NewEntry[10];
NewEntry[0] = &cnName;
NewEntry[1] = &FName;
NewEntry[2] = &OClass;
NewEntry[3] = NULL;
rc = ldap_add_s(ld, entry_dn, NewEntry);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_search_s: rc: %d\n", rc);
ldap_unbind_s(ld);
if (res != NULL)
ldap_msgfree(res);
return -1;
}
当添加证书数据时,使用以上方式无法添加成功,调用方式会有所区别
//Qt方式读取文件
QByteArray fileData;
QFile file("C:/Users/Songxd/Desktop/jj.cer");
if(file.open(QIODevice::ReadOnly)) {
fileData = file.readAll();
file.close();
}
struct berval cert_berval;
struct berval *cert_values[2];
LDAPMod cert_attribute;
cert_attribute.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
//证书的属性类型为userCertificate;binary
cert_attribute.mod_type = (PWCHAR)L"userCertificate;binary";
cert_berval.bv_len = fileData.size();
cert_berval.bv_val = fileData.data();
cert_values[0] = &cert_berval;
cert_values[1] = NULL;
cert_attribute.mod_bvalues = cert_values;
为什么证书需要用下面这种方式才能添加成功
我们到官方接口文档中找答案
https://docs.microsoft.com/en-us/windows/win32/api/winldap/ns-winldap-ldapmodw
先看ldapmod的结构
typedef struct ldapmodW {
ULONG mod_op;
PWCHAR mod_type;
union {
PWCHAR *modv_strvals;
struct berval **modv_bvals;
} mod_vals;
} LDAPModW, *PLDAPModW;
这两种用法的区别就是value值使用modv_strvals和modv_bvals的区别
文档中说明
Pointer to an array of values, if any, to add, delete, or replace. If mop_op does not include the LDAP_MOD_BVALUES flag, the modv_strvals member is a pointer to an array of null-terminated strings. If mop_op includes LDAP_MOD_BVALUES, the modv_bvals member is a pointer to an array of berval pointers, which is useful for specifying binary values
如果mop_op不包含LDAP_MOD_BVALUES标志,则modv_strvals成员是指向空终止字符串数组的指针。如果mop_op包含LDAP_MOD_BVALUES,则modv_bvals成员是指向一个berval指针数组的指针,这对于指定二进制值很有用。
最后一句话说明了传二进制数据要用modv_bvals,证书数据使用userCertificate;binary,明确了使用二进制数据,即der的证书格式,尝试使用pem证书无法直接添加成功。
删除
删除就很简单了,传参要删的dn即可
PWSTR entry_dn = (PWSTR)L"cn=testCert,c=cn";
rc = ldap_delete_s(ld, entry_dn);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_search_s: rc: %d\n", rc);
ldap_unbind_s(ld);
if (res != NULL)
ldap_msgfree(res);
return -1;
}
修改
修改和新增是相通的
LDAPMod Name, OClass, FName, LName, Title, Phone, Uid;
PWSTR entry_dn = (PWSTR)L"cn=test22,c=cn";
LDAPMod *NewEntry[3];
const wchar_t *ho_values[] = {L"123456", NULL};
//使用LDAP_MOD_REPLACE和LDAP_MOD_ADD都可以
Uid.mod_op = LDAP_MOD_REPLACE;
Uid.mod_type = (PWSTR)L"uid";
Uid.mod_values = (PWSTR *)ho_values;
NewEntry[0] = &Uid;
NewEntry[1] = NULL;
rc = ldap_modify_s(ld, entry_dn, NewEntry);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_search_s: rc: %d\n", rc);
ldap_unbind_s(ld);
if (res != NULL)
ldap_msgfree(res);
return -1;
}
结语
以上的ldap连接及增删改的内容,查询功能将在下一篇再进行介绍,毕竟查询是ldap的主打功能