LDAP应用技术简述
2010-03-30 01:06:10 阿炯

一、简介
a) X500目录服务
OSL X.500目录是基于OSI网络协议的目录服务协议,也是LDAP的前身。但是X500的缺点是不支持TCP/IP,而是支持OSI协议,显然,在 Windows等个人电脑上不可以使用OSI协议,在此前提下,也就产生了访问X500目录的网关--LDAP。


b) LDAP
LDAP(Lightweight Directory Access Protocal, 轻型目录访问协议),是针对以X500目录为主的目录服务的前端访问协议,是OSL X.500目录访问网关。由于X500原来不是为TCP/IP网络设计的,而目录服务的最大使用者偏偏是TCP/IP客户,因此,LDAP就被设计成使用 TCP/IP访问OSI 目录服务的服务协议,而随着互联网成为网络的主流,LDAP也成为一个具备目录的大部分服务的协议。


LDAP主要解决目录服务的前端访问形式,而不是对目录服务本身制定的的协议,理论上,LDAP支持后台的任何存储形式,包括X500,关系数据库,文本数据库或文件目录等。LDAP继承了X500目录的大部分定义,无论是访问样式还是语法都与X500相似。


c) Active Directory 活动目录
Active Directory (AD)是微软为.net中的对象访问定义的目录服务,包括目录服务本身,以及客户端API(ADSI)。Ad并不是LDAP在.net中的实现,而是 X500在.net中的实现,但AD前端支持并主要以LDAP形式进行访问。完整地说,AD是基于微软自身定义的X500扩展的一系列Schema实现的 X500目录服务及相关的访问控制工具的集合,其前端支持LDAP的查询,目的是对.net中涉及的所有网络对象提供目录服务。各个schema在一个树森林中是唯一的。


普通的LDAP客户端工具与AD并不兼容。WINDOWS2000自带有一些LDAP客户端工具,包括ldifde.exe, ldp.exe。并提供专门的LDAP程序接口ASDI。同时,可以在WINDOWS管理台上添加AD管理snap-in,配合已有的AD基本管理工作。使用以上工具可以得到微软样式的详情,但总的来说,WINDOWS2000原则上不鼓励用户在AD的基础上进一步的开发,没有开发更多的资料。


WINDOWS2000中,访问AD记录的API被集成到了内核,服务于WINDOWS2000从主机权限和对象管理,直接网络的权限和对象管理,同时API细节没有对外公开。因此,某种程度上,AD是一个只对WINDOWS2000有用的目录服务,AD连同访问API,形成一个基于 X500-LDAP的孤岛,从一开始就没有打算与其他厂商产品有兼容的余地,这也是微软的一贯风格。参考:
http://www.microsoft.com/windows2000/en/server/help/default.asp? url=/windows2000/en/server/help/sag_ADschema_Intro.htm
http://msdn.microsoft.com/library/default.asp? url=/library/en-us/netdir/ad/schema_implementation.asp
AD在WINDOWS2000中注册表中的位置是:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NTDS\


使用AD时,用户可以自行在微软样式的基础上添加新的类和属性,微软称这个就是schema的增添,这与UNIX环境下有一些不同,用户余地较少。如果真的需要添加,可以使用按:http://www.microsoft.com/ windows2000/techinfo/planning/activedirectory/adschemasteps.asp


的指示一步步做,也可以预先做好ldif文件,使用ldifde.exe一次性地进行添加,效果是一样的。在默认的状况下,WINDOWS2000的AD初始具备三个上下文对象:
dc=domainname; 微软定义domainname必须是examples.com格式,即dc=example,dc=com;
cn=Configuration,dc=example,dc=com; 这一条目和上下文存储设置信息;
cn=schema,cn=configuration,dc=example,dc=com;

二、服务器实现方式
用户可以选择购买商业的LDAP服务器,如SUN的iplanet directory server;但在大部分情况下,使用openldap足以完成所需要的目录服务工作。另外,包括windows 2000以及如domino6这样的系统软件中,通常也都集成了一个自身使用的LDAP服务器。

三、数据结构原理
不少LDAP开发人员喜欢把LDAP与关系数据库相比,认为是另一种的存贮方式,然后在读性能上进行比较。实际上,这种对比的基础是错误的。LDAP和关系数据库是两种不同层次的概念,后者是存贮方式(同一层次如网格数据库,对象数据库),前者是存贮模式和访问协议。LDAP是一个比关系数据库抽象层次更高的存贮概念,与关系数据库的查询语言SQL属同一级别。LDAP是实现了指定的数据结构的存贮,它包括以下可以用关系数据库实现的结构要求:树状组织、条目认证、类型定义、许可树形记录拷贝。


a) 树状组织
无论是X500还是LDAP都是采用树状方式进行记录。每一个树目录都有一个树根的入口条目,子记录全部是这一根条目的子孙。这是目录与关系数据类型最大的区别(关系数据库的应用结构也可实现树状记录)。因此,把目录看作是更高级的树状数据库也未尝不可,只不过除此外,它不能实现关系存贮的重要功能。


b) 条目和条目认证
LDAP是以条目作为认证的根据。ROOT的权限认证与目录本身无关,但除此外所有条目的认证权限由条目本身的密码进行认证。LDAP可以配置成各种各样不同的父子条目权限继承方式。


每一个条目相当于一个单一的平面文本记录,由条目自身或指定的条目认证进行访问控制。因此,LDAP定义的存贮结构等同于一批树状组织的平面数据库,并提供相应的访问控制。


条目中的记录以名-值对的形式存在,每一个名值对必须由数据样式schema预定义。因此,LDAP可以看作是以规定的值类型以名值对形式存贮在一系列以树状组织的平面数据库的记录的集合。


c) 数据样式(schema)
数据样式schema是针对不同的应用,由用户指定(设计)类和属性类型预定义,条目中的类(objectclass)和属性必须在在 LDAP服务器启动时载入内存的schema已有定义。因此,AD活动目录中的条目记录就必须符合Active Directory的schema中。如果已提供的schema中的定义不够用,用户可以自行定义新的schema,在这里 中可以看到常用的schema。


d) 类型分类(objectClass)
条目中的记录通过objectclass实现分类,objectClass是一个继承性的类定义,每一个类定义指定必须具备的属性。如某一条目指定必须符合unit类型,则它必须具备chinacfirm类形指定的属性,象法人代表什么的。通过objectclass分类,分散的条目中的记录就实际上建立了一个索引结构,为高速的读查询打下了基础。Objectclass也是过滤器的主要查询对象。


e) 过滤器和语法
LDAP是一个查询为主的记录结构,无论是何种查询方式,最终都由过滤器缺点查询的条件。过滤器相当于SQL中的WHERE子句。任何LDAP的类过滤和字符串都必须放在括号内,如(objectclass=*),指列出所有类型的记录(不过分类)。


可以使用=,>=,<=,~=(约等于)进行比较,如(number<=100)。合并条件是最怪的,必须把操作符放在两个操作对象的前面而不是中间,单一操作对象用括号括起来。如
A与B,不是A&B,而是(&(A)(B))。
或使用"|"表示;
非使用"!"表示。
对于"与",或"或"在操作符后可以跟多个条件表达式,但非后则只参是单个表达式。详见RFC1558。


f) 树移植
LDAP最重要的特性和要求并不是读性能,而是扩展性。这一特性是通过树移植和树复制实现的。按LDAP的RFC要求,LDAP目录应该可以任意地在不同的目录间连接、合并并实现自动复制,及自动性同步。这意味着用户可以在任一LDAP中访问条目,而不用管其中某一部分是否复制自全世界另一目录中的记录,同时另一目录中的记录同样在正常运作。


这一特性如果在关系数据库中实现,意味着要使用程序化的非规范化预复制。类似于汇总帐目的设计。


g) LDIF交换文件
LDIF是LDAP约定的记录交换格式,以平面文本的形式存在,是大部分LDAP内容交换的基础,如拷贝、添加、修改等操作,都是基于LDIF文件进行操作。


f) JAVA或CORBA对象串行化存储
网络高效率的访问加上JAVA的跨平台能力,当把JAVA或CORBA对象串行化后存储到LDAP目录上时,可以产生非同一般的集成效果--实际上,这正是EJB和.NET的网络定位基础技术。


使用JAVA或CORBA对象存储时,必须首先让LDAP服务支持该对象定义,也就是说包含qmail.schema或corba.schema。


JAVA必须存储在objectclass=javacontainer的条目中,而且必须带有cn属性,这意味着除非该JAVA类专门实现了DirContext接口,对于大多数JAVA类来说,只能采用DirContext代替Context实现bind的添加操作。取出JAVA类相对要简单得多,只需使用context.lookup()获得该对象的句柄,然后强制造型成所需要的对象就可以了,如:
Person p=(Person)contex.lookup("cn=elvis,dc=daifu,dc=com");
这个句法在EJB的程序中,是经常用到的。


使用CORBA的跨语言性质,使用CORBA存储对象比JAVA更加诱人,这意味着所存储的对象可以被任何语言编写的客户端访问。其实,微软的.net说到底也非常简单,无非是把COM对象存储到微软自家的目录ActiveDirectory里面,从而可以在网络范围内使用任何微软平台的语言进行对象访问而已。众所周知,COM就是与CORBA相对的微软规范。


使用对象串行化技术,可以把常用对象如某个打印机,某个客户直接存储到LDAP中,然后快速获取该对象的引用,这样,就比把对象信息存储到关系数据库中,分别取出属性,然后再初始化对象操作的做法,效率要高得多了。这是LDAP目前比普通关系数据库存储要优秀的地方,而对象数据库还不成熟。

客户端访问工具
a)openldap命令行;
Openldap提供了在UNIX命令行下的访问工具集。包括ldapsearch,ldapadd,ldapmodify,ldappassword,ldapdelete等必要的工具。除了使用man获得使用帮助外,还可以在'http://www.tldp.org/HOWTO/LDAP-HOWTO/'获得使用的支持。

例子:通过查询根上下文判断LDAP服务器是否正常工作:
$ ldapsearch -x -b "" -s base "objectclass=*" namingContexts

注:该命令查询该当前服务器上的命令上下文,通常就是rootdn的上下文记录。
-x    指该查询使用目录认证而不是使用SASL认证,;
-b "" 是查询的起点,即base,空指从根开始查询;
-s base 指查询范围。有三种选项,one指一层,包括兄弟条目;base指当前条目,sub,子孙记录。默认是sub.
"objectclass=*" 是过滤器,表示所有记录类型都加以选择;
namingContexts是约定的特殊属性,可以选择其他属性值进行查询。
$ ldapsearch -x -b "dc=daifu,dc=com" -s base "objectclass=organization" dn dc

注:
-b "dc=daifu,dc=com" 指查询的是"dc=daifu,dc=com"的条目,需要注意的是,slapd.conf中指定rootdn为"dc=daifu,dc=com",并不等同于目录中已经具有真实的"dc=daifu,dc=com"条目。

"objectclass=organization" 指查询条件是organization类的。
"dn dc"指只需列出dn,dc两项属性。


 (ldapadd),ldapmodify的操作是基于LDIF文件的,所以必须先按规则生成LDIF文本文件,然后执行导入。要注意的是,新装的LDAP具备一个上下文,并不等于在目录中有相应的条目。如,OPENLDAP的slapd.conf中已经定义了一个根"dc=daifu,dc=com",并不等于可以把新的条目添加到"dc=daifu,dc=com",因此实际上并没有这一条目,必须先执行添加相应的条目,然后才可以添加后续条目。其次,LDIF的格式文件非常严格,空间被认为是确定的字符,因此,需要特别注意每行后面不应带有空格。

b) ldapbrowser
ldapbrowser是开源的LDAP浏览工具,并带有不太强的条目编辑功能。Ldapbrowser是纯JAVA的程序,可跨平台运行,在开源的程序中,是最优秀的一个。缺点是不能浏览服务器端的schema对象,从而限制了条目编辑(添加)的能力。


其次,由于JAVA对LDAP的访问方式在添加时,必须预先生成一个实现DirContext接口的类,因此,这也令订制型的添加操作在JAVA实现时相当困难。

c)ldapadministrator
ldapadministrator是一个windows的收费程序,试用一个月。Ldapadministrator除了具备ldapbrowser的功能外,在条目编辑上的功能大为增强。但从另一个角度看,LDAP总是涉及到大量的条目,当需要编辑的条目急速增加时,使用ldapadminstrator就不是轻松的事情,此时还是使用LDIF文件交换为佳。

d)浏览器
根据rfc2255.txt的约定,可以使用URI定义LDAP查询,因此,理论上,只要浏览器内嵌支持,就可以作为LDAP客户端使用。IE浏览器支持简单的LDAP查询。此时,IE把LDAP的URI看作是查询的对象。但是IE以及Exchange server非常狭隘地把LDAP看作是纯粹为EMAIL地址查询服务的,只能以"图形"的方式显示查到的邮件地址什么的。因此,IE准确地说,是对LDAP存储的邮件地址信息的查询工具。

URI查询语法是:
ldapurl    = scheme "://" [hostport] ["/"
[dn ["?" [attributes] ["?" [scope]
["?" [filter] ["?" extensions]]]]]]
scheme     = "ldap"
attributes = attrdesc *("," attrdesc)
scope      = "base" / "one" / "sub"
dn         = distinguishedName from Section 3 of [1]
hostport   = hostport from Section 5 of RFC 1738 [5]
attrdesc   = AttributeDescription from Section 4.1.5 of [2]
filter     = filter from Section 4 of [4]
extensions = extension *("," extension)
extension  = ["!"] extype ["=" exvalue]
extype     = token / xtoken
exvalue    = LDAPString from section 4.1.2 of [2]
token      = oid from section 4.1 of [3]
xtoken     = ("X-" / "x-") token
例:
类似
#ldapsearch -x -h 192.168.0.2 -p 389 -b "dc=daifu,dc=com" -b "sub" "objectclass=qmailuser"

的URI查询是:
ldap://192.168.0.2:389/dc=daifu,dc=com??sub?objectclass=qmailuser?
浏览器并不是完全的LDAP客户工具。

e)ldapexplorer(PHP)
一个用PHP写的LDAP处理工具 

二、编写LDAP访问程序;
a) JAVA
i. JNDI(JAVA 命名及目录接口)
JNDI是JAVA为命名及目录服务访问制定的基础接口标准,用于访问包括DNS,NIS,LDAP,文件系统等任何以树目录形式存在目标对象,并且基本上可保持访问方式的一致(意味着代码共用)。对于不同的目录类型,JNDI通过使用不同的目录驱动,以保证访问方式的一致。


以下连接可获得较详细的讲解和例程:http://java.sun.com/products/jndi/tutorial/
经常使用的LDAP驱动有JDK自带的LDAP provider,还有IBM的PROVIDER,以及NOVEL的JNDI产品。需要提醒的是,JNDI中最重要的概念是上下文context,即定位从那一个入口开始操作,相当于openldap命令行中的-D开关的作用。其他的操作都是该上下文对象的调用。


LDAP通过JNDI添加条目是基于DirContext类的操作。由于JAVA只能以对象方式向LDAP添加条目,因此,必须首先具备实现DirContext接口的类,或使用DirContext(扩充了context接口)代替context,然后才能通过ctx.bind()的方法把该类添加到目录中。

JAVA-LDAP-ADD的操作可以有三种方式:
Context.bind()或ctx.createSubcontext("name");
或DirContext.bind("dn",attrs,object)的方式。对于没有预先编写实现DirContext接口的类对象的添加,这是唯一的办法。

ii.JLDAP
JLDAP是由novel开发的,原是针对Novel的NDS目录设计的JAVA访问工具。NOVEL的NDS和网景(NETSCAPE)的目录是工具界最早的目录产品。JLDAP并非JNDI的服务供应者,而是同一抽象层次下的访问工具集。与JNDI-LDAP相比,JLDAP更接近于类关系数据库的访问方式。


NDS是遵守LDAP协议的并进行了扩展的类MAD产品。而NOVEL也已把JLDAP捐献给了OPENLDAP开源项目,可以世界范围内自由使用。与JNDI相比,JLDAP无须继承DirContext才能实现添加,也无需预先生成添加的类,可以象普通数据访问那样,生成连接,然后使用::add方法添加。这样,添加的灵活性要强于JNDI。


但由于JLDAP目前是访问NDS,因此,它不具备JNDI完全面向对象存储的能力,对于高级的LDAP应用,JLDAP不是合适的选择。
例:
import com.novell.ldap.*;
public class AddEntry{
public static void main( String[] args ){

if (args.length != 4) {
System.err.println("Usage:   java AddEntry "
+ " ");
System.err.println("Example: java AddEntry Acme.com"
+ " \"cn=admin,o=Acme\" secret \"ou=Sales,o=Acme\"");
System.exit(1);
}
int ldapPort = LDAPConnection.DEFAULT_PORT;
int ldapVersion  = LDAPConnection.LDAP_V3;
String ldapHost       = args[0];
String loginDN        = args[1];
String password       = args[2];
String containerName  = args[3];
LDAPConnection lc = new LDAPConnection();
LDAPAttribute  attribute = null;
LDAPAttributeSet attributeSet = new LDAPAttributeSet();
/* To Add an entry to the directory,
*   -- Create the attributes of the entry and add them to an attribute set
*   -- Specify the DN of the entry to be created
*   -- Create an LDAPEntry object with the DN and the attribute set
*   -- Call the LDAPConnection add method to add it to the directory
*/         
String objectclass_values[] = { "inetOrgPerson" };
attribute = new LDAPAttribute( "objectclass", objectclass_values );
attributeSet.add( attribute );    
String cn_values[] = { "James Smith", "Jim Smith", "Jimmy Smith" };
attribute = new LDAPAttribute( "cn", cn_values );
attributeSet.add( attribute );
String givenname_values[] = { "James", "Jim", "Jimmy" };
attribute = new LDAPAttribute( "givenname", givenname_values );
attributeSet.add( attribute );
attributeSet.add( new LDAPAttribute( "sn", "Smith" ) );
attributeSet.add( new LDAPAttribute( "telephonenumber",
"1 801 555 1212" ) );

attributeSet.add( new LDAPAttribute( "mail", "JSmith@Acme.com" ) );
String  dn  = "cn=JSmith," + containerName;    
LDAPEntry newEntry = new LDAPEntry( dn, attributeSet );
try {
// connect to the server
lc.connect( ldapHost, ldapPort );
// authenticate to the server
lc.bind( ldapVersion, loginDN, password );
lc.add( newEntry );
System.out.println( "\nAdded object: " + dn + " successfully." );
// disconnect with the server
lc.disconnect();
}
catch( LDAPException e ) {
System.out.println( "Error:  " + e.toString());
}                                 
System.exit(0);
}
}

iii.JdbcLDAP
JDBCLDAP是OcterString提供的,能过类SQL实现LDAP访问的工具。JDBCLDAP是针对大量熟悉SQL而对LDAP欠缺了解的程序员而设计的,可以完成简单的LDAP查询、插入、更新、删除这样的工作。


JdbcLDAP使用LDAP-JDBC驱动访问"LDAP数据库":
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
连接时使用各个DN的具体权限建立连接:
String ldapConnectString =   "jdbc:ldap://localhost:389/dc=examples,dc=com?SEARCH_SCOPE:=subTreeScope";
java.sql.Connection con;
con = DriverManager.getConnection(ldapConnectString,"cn=Admin","manager");


连接字符串遵从标准的LDAP-URL格式,(RFC2255)。SQL操作时,将每一个目录ENTRY看作是一个统一表的一行,然后把属性看作列,如:
String SQL = "INSERT INTO cn,ou (objectClass,objectClass,objectClass,ou,sn,cn) " +
"VALUES (top,person,organizationalPerson,Product Development,Boorshtein," +
"Marc Boorshtein)";
Statement insert = con.createStatement();
int count = insert.executeUpdate(SQL);
if (count < 1) {
System.out.println("Insert Failed");
} else {
System.out.println("Insert Succeeded");
}
Ou,sn,cn是新条目的入口标识。


Jdbc-LDAP不可以完成串行化binding,因此,只适宜对已有的LDAP进行临时访问(如程序员不熟悉,或保持旧程序,仅修改必要的连接项),不宜把整个项目建筑在JDBC-LDAP上。实际上,LDAP本身就是一种访问的前端协议,硬要把SQL再作为前端使用,是完全没有必要的。

b)C语言
包括openldap,netscape(sun),mozilla, novell,ibm等,都提供了LDAP的C SDK和接口函数。作为RFC标准的LDAP结构,struct LDAP是定义为对用户隐藏的,并在各个实现函数中各自定义,以便适应不同的LDAP访问。


#typedef struct ldap LDAP在ldap.h中定义;在2.0版以后,struct LDAP改为隐藏,只能能过函数ldap_set_option 和ldap_get_option访问。(draft-ldapext-ldap-c-api-xx.txt)

使用时:
如:
LDAP *Ld=null;  //声明并初始化LDAP类型的变量指针 *ld;
Ld       =ldap_init( ldaphost, ldapport );  //获取LDAP的会话;
获得会话后,调用ldap_simple_bind_s获得访问LDAP的权限,然后就可以调用不同的函数,如
ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,sctrls, cctrls, timeout, sizelimit, &msgid );

即可完成相关的操作。
即步骤为:1、获得会话;2、绑定对象;3、。执行操作。

连接例子:
#include "stdio.h"
#include "ldap.h"
/* Adjust these setting for your own LDAP server */
#define HOSTNAME "localhost"
#define PORT_NUMBERLDAP_PORT
#define FIND_DN "uid=bjensen, ou=People, o=Airius.com"

int main( int argc, char **argv ) {

LDAP*ld;
LDAPMessage*result, *e;
BerElement*ber;
char*a;
char**vals;
int i, rc;
/* Get a handle to an LDAP connection. */
if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL ) {
perror( "ldap_init" );

return( 1 );
}

/* Bind anonymously to the LDAP server. */
rc = ldap_simple_bind_s( ld, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc));
return( 1 );
}

/* Search for the entry. */
if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE,
"(objectclass=*)", NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
LDAP_NO_LIMIT, &result ) ) != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc));
return( 1 );
}

/* Since we are doing a base search, there should be only one matching entry. */

e = ldap_first_entry( ld, result );
if ( e != NULL ) {
printf( "\nFound %s:\n\n", FIND_DN );
/* Iterate through each attribute in the entry. */
for ( a = ldap_first_attribute( ld, e, &ber );
a != NULL; a = ldap_next_attribute( ld, e, ber ) ) {
/* For each attribute, print the attribute name and values. */
if ((vals = ldap_get_values( ld, e, a)) != NULL ) {
for ( i = 0; vals[i] != NULL; i++ ) {
printf( "%s: %s\n", a, vals[i] );
}
ldap_value_free( vals );
}
ldap_memfree( a );
}

if ( ber != NULL ) {
ber_free( ber, 0 );
}
}

ldap_msgfree( result );
ldap_unbind( ld );
return( 0 );
}

i.Novell函数库
Novel提供了基于普通LDAP函数库的扩展,主要包括两个部分:针对Novel eDirectory服务器产品的扩展,其次是对如ldapsearch等常用函数的扩展。详情可从:http://developer.novell.com/ndk/qstart/opensource.htm#ldapc  获得帮助。

ii.Netscape函数库
Netscape一度是企业级目录服务提供者,许多LDAP的C例子,实际上都是基于Netscape服务器的。但在Netscape被收购后,其目录服务成了iPlanet和SUN eDirectory产品的一部分,出于支持JAVA和iplanet产品的缘故,SUN对该产品和相关库的支持远不够积极,特别是对linux的支持不够充分,估计也与保护solaris产品有关。

iii.Mozilla函数库
Mozilla可以看作是Netscape的另一个分支。准确地说,Netscape本来就是源于Mozilla。Mozilla是也是一个开源的项目,提供完整的C-SDK,缺点是对linux的支持不够充分。

c)Perl接口
Perl 的NET::LDAP模块中包括有完整的LDAP目录访问函数,只要安装NET::LDAP就可以完成正常的LDAP目录访问;但在安装NET::LDAP模块前,必须先安装Convert::ASN1模块,该模块可以从CPAN下载。例:
#!/usr/bin/perl
use warnings;
use strict;
use Net::LDAP;
use Net::LDAP::Util qw(ldap_error_text);
my $server = "localhost";
my $ldap = new Net::LDAP($server) ||
die("failed to connect to server.$!\n");

my $mesg = $ldap->bind("cn=Manager,dc=daifu,dc=com", password => "secret");
die ("bind failed with ",ldap_error_text($mesg->code()),"\n")
if $mesg->code();
$mesg = $ldap->search(base => "dc=daifu,dc=com", scope => "sub",
filter => "sn=*",);
die ("search failed with ",ldap_error_text($mesg->code()),"\n")
if $mesg->code();
print "Count is ",$mesg->count(),"\n";

while (my $entry = $mesg->shift_entry()) {
print "dn:",$entry->dn(),"\n";
for my $attr($entry->attributes()) {
for my $val($entry->get_value($attr)) {
print "$attr:$val\n";
}
}
print "\n";
}

操作过程实际上与C和JAVA是一样的。

该文章最后由 阿炯 于 2012-06-11 16:48:32 更新,目前是第 3 版。