LDAP超详细资料

LDAP超详细资料

ID:37853878

大小:214.50 KB

页数:37页

时间:2019-06-01

上传者:U-2437
LDAP超详细资料_第1页
LDAP超详细资料_第2页
LDAP超详细资料_第3页
LDAP超详细资料_第4页
LDAP超详细资料_第5页
资源描述:

《LDAP超详细资料》由会员上传分享,免费在线阅读,更多相关内容在教育资源-天天文库

LDAP技术总结(本文档由廖武锋编写)第一章LDAP有关技术介绍第一节X.500目录服务OSLX.500目录是基于OSI网络协议的目录服务协议,也是LDAP的前身。但是X.500的缺点是不支持TCP/IP,而是支持OSI协议,显然,在Windows等个人电脑上不可以使用OSI协议,在此前提下,也就产生了访问X.500目录的网关-LDAP。第二节什么是LDAP?LDAP英文全称是LightWeightDirectoryAccessProtocol,一般都简称为LDAP。它是基于X.500标准的,但是简单多了并且可以根据需要定制。与X.500不同,LDAP支持TCP/IP,这对访问Internet是必须的。LDAP的核心规范在RFC中都有定义,所有与LDAP相关的RFC都可以在LDAPmanRFC网页中找到。现在LDAP技术不仅发展得很快而且也是激动人心的。在企业范围内实现LDAP可以让运行在几乎所有计算机平台上的所有的应用程序从LDAP目录中获取信息。LDAP目录中可以存储各种类型的数据:电子邮件地址、邮件路由信息、人力资源数据、公用密匙、联系人列表等等。通过把LDAP目录作为系统集成中的一个重要环节,可以简化员工在企业内部查询信息的步骤,甚至连主要的数据源都可以放在任何地方。第三节SunOneDirectoryServer目录服务第四节WindowsActiveDirectory活动目录ActiveDirectory(AD)是微软为.net中的对象访问定义的目录服务,包括目录服务本身,以及客户端API(ADSI)。AD并不是LDAP在.net中的实现,而是X.500在.net中的实现,但AD前端支持并主要以LDAP形式进行访问。完整地说,AD是基于微软自身定义的X.500扩展的一系列Schema实现的X.500目录服务及相关的访问控制工具的集合,其前端支持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,形成一个基于X.500-LDAP的孤岛,从一开始就没有打算与其他厂商产品有兼容的余地,这也是微软的一贯风格。参考:http://www.microsoft.com/windows2000/en/server/help/default.asp?url=/windows2000/en/server/help/sag_ADschema_Intro.htmhttp://msdn.microsoft.com/library/default.asp?url=/library/en-us/netdir/ad/schema_implementation.aspAD在Windows2000中注册表中的位置是:HKEY_LOCAL_MACHINESystemCurrentControlSetServicesNTDS使用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;这一上下文的条目存储schema设计信息;第五节LDAP目录服务的优势如果需要开发一种提供公共信息查询的系统一般的设计方法可能是采用基于WEB的数据库设计方式,即前端使用浏览器而后端使用WEB服务器加上关系数据库。后端在Windows的典型实现可能是WindowsNT+IIS+Acess数据库或者是SQLServer服务器,IIS和数据库之间通过ASP技术使用ODBC进行连接,达到通过填写表单查询数据的功能;后端在Linux系统的典型实现可能是Linux+Apache+postgresql,Apache和数据库之间通过PHP3提供的函数进行连接。使用上述方法的缺点是后端关系数据库的引入导致系统整体的性能降低和系统的管理比较繁琐,因为需要不断的进行数据类型的验证和事务的完整性的确认;并且前端用户对数据的控制不够灵活,用户权限的设置一般只能是设置在表一级而不是设置在记录一级。目录服务的推出主要是解决上述数据库中存在的问题。目录与关系数据库相似,是指具有描述性的基于属性的记录集合,但它的数据类型主要是字符型,为了检索的需要添加了BIN(二进制数据)、CIS(忽略大小写)、CES(大小写敏感)、TEL(电话型)等语法(Syntax),而不是关系数据库提供的整数、浮点数、日期、货币等类型,同样也不提供象关系数据库中普遍包含的大量的函数,它主要面向数据的查询服务(查询和修改操作比一般是大于10:1),不提供事务的回滚(rollback)机制,它的数据修改使用简单的锁定机制实现All-or- Nothing,它的目标是快速响应和大容量查询并且提供多目录服务器的信息复制功能。现在该说说LDAP目录到底有些什么优势了。现在LDAP的流行是很多因数共同作用的结果。可能LDAP最大的优势是:可以在任何计算机平台上,用很容易获得的而且数目不断增加的LDAP的客户端程序访问LDAP目录。而且也很容易定制应用程序为它加上LDAP的支持。LDAP协议是跨平台的和标准的协议,因此应用程序就不用为LDAP目录放在什么样的服务器上操心了。实际上,LDAP得到了业界的广泛认可,因为它是Internet的标准。产商都很愿意在产品中加入对LDAP的支持,因为他们根本不用考虑另一端(客户端或服务端)是怎么样的。LDAP服务器可以是任何一个开发源代码或商用的LDAP目录服务器(或者还可能是具有LDAP界面的关系型数据库),因为可以用同样的协议、客户端连接软件包和查询命令与LDAP服务器进行交互。与LDAP不同的是,如果软件产商想在软件产品中集成对DBMS的支持,那么通常都要对每一个数据库服务器单独定制。不象很多商用的关系型数据库,你不必为LDAP的每一个客户端连接或许可协议付费大多数的LDAP服务器安装起来很简单,也容易维护和优化。LDAP服务器可以用“推”或“拉”的方法复制部分或全部数据,例如:可以把数据“推”到远程的办公室,以增加数据的安全性。复制技术是内置在LDAP服务器中的而且很容易配置。如果要在DBMS中使用相同的复制功能,数据库产商就会要你支付额外的费用,而且也很难管理。LDAP允许你根据需要使用ACI(一般都称为ACL或者访问控制列表)控制对数据读和写的权限。例如,设备管理员可以有权改变员工的工作地点和办公室号码,但是不允许改变记录中其它的域。ACI可以根据谁访问数据、访问什么数据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由LDAP目录服务器完成的,所以不用担心在客户端的应用程序上是否要进行安全检查。LDAP(LightweightDirectoryAcessProtocol)是目录服务在TCP/IP上的实现(RFC1777V2版和RFC2251V3版)。它是对X.500的目录协议的移植,但是简化了实现方法,所以称为轻量级的目录服务。在LDAP中目录是按照树型结构组织,目录由条目(Entry)组成,条目相当于关系数据库中表的记录;条目是具有区别名DN(DistinguishedName)的属性(Attribute)集合,DN相当于关系数据库表中的关键字(PrimaryKey);属性由类型(Type)和多个值(Values)组成,相当于关系数据库中的域(Field)由域名和数据类型组成,只是为了方便检索的需要,LDAP中的 Type可以有多个Value,而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织,非常的直观。LDAP把数据存放在文件中,为提高效率可以使用基于索引的文件数据库,而不是关系数据库。LDAP协议集还规定了DN的命名方法、存取控制方法、搜索格式、复制方法、URL格式、开发接口等。LDAP对于这样存储这样的信息最为有用,也就是数据需要从不同的地点读取,但是不需要经常更新。例如,这些信息存储在LDAP目录中是十分有效的:1公司员工的电话号码簿和组织结构图2客户的联系信息3计算机管理需要的信息,包括NIS映射、email假名,等等4软件包的配置信息5公用证书和安全密匙什么时候该用LDAP存储数据大多数的LDAP服务器都为读密集型的操作进行专门的优化。因此,当从LDAP服务器中读取数据的时候会比从专门为OLTP优化的关系型数据库中读取数据快一个数量级。也是因为专门为读的性能进行优化,大多数的LDAP目录服务器并不适合存储需要需要经常改变的数据。例如,用LDAP服务器来存储电话号码是一个很好的选择,但是它不能作为电子商务站点的数据库服务器。如果下面每一个问题的答案都是“是”,那么把数据存在LDAP中就是一个好主意。第一需要在任何平台上都能读取数据吗?第二每一个单独的记录项是不是每一天都只有很少的改变?第三可以把数据存在平面数据库(flatdatabase)而不是关系型数据库中吗?换句话来说,也就是不管什么范式不范式的,把所有东西都存在一个记录中(差不多只要满足第一范式)。第四最后一个问题可能会唬住一些人,其实用平面数据库去存储一些关系型的数据也是很一般的。例如,一条公司员工的记录就可以包含经理的登录名。用LDAP来存储这类信息是很方便的。一个简单的判断方法:如果可以把保数据存在一张张的卡片里,就可以很容易地把它存在LDAP目录里。第六节LDAP安全和访问控制LDAP提供很复杂的不同层次的访问控制或者ACI。因这些访问可以在服务器端制,这比用客户端的软件保证数据的安全可安全多了。用LDAP的ACI,可以完成:第一给予用户改变他们自己的电话号码和家庭地址的权限,但是限制他们对其它数据(如,职务名称,经理的登录名,等等)只有“只读”权限。第二给予“HR-admins"组中的所有人权限以改变下面这些用户的信息:经理、工作名称、员工号、部门名称和部门号。但是对其它域没有写权限。第三禁止任何人查询LDAP服务器上的用户口令,但是可以允许用户改变他或她自己的口令。第四 给予经理访问他们上级的家庭电话的只读权限,但是禁止其他人有这个权限。第五给予“host-admins"组中的任何人创建、删除和编辑所有保存在LDAP服务器中的与计算机主机有关的信息第六通过Web,允许“foobar-sales"组中的成员有选择地给予或禁止他们自己读取一部分客户联系数据的读权限。这将允许他们把客户联系信息下载到本地的笔记本电脑或个人数字助理(PDA)上。(如果销售人员的软件都支持LDAP,这将非常有用)第七通过Web,允许组的所有者删除或添加他们拥有的组的成员。例如:可以允许销售经理给予或禁止销售人员改变Web页的权限。也可以允许邮件假名(mailaliase)的所有者不经过IT技术人员就直接从邮件假名中删除或添加用户。“公用”的邮件列表应该允许用户从邮件假名中添加或删除自己(但是只能是自己)。也可以对IP地址或主机名加以限制。例如,某些域只允许用户IP地址以192.168.200.*开头的有读的权限,或者用户反向查找DNS得到的主机名必须为*.foobar.com。第七节LDAP目录树结构LDAP目录以树状的层次结构来存储数据。如果你对自顶向下的DNS树或UNIX文件的目录树比较熟悉,也就很容易掌握LDAP目录树这个概念了。就象DNS的主机名那样,LDAP目录记录的标识名(DistinguishedName,简称DN)是用来读取单个记录,以及回溯到树的顶部。后面会做详细地介绍。为什么要用层次结构来组织数据呢?原因是多方面的。下面是可能遇到的一些情况:第一如果你想把所有的美国客户的联系信息都“推”到位于到西雅图办公室(负责营销)的LDAP服务器上,但是你不想把公司的资产管理信息“推”到那里。第二你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里,资产管理组对“asset-mgmt"部分有完全的访问权限,但是不能访问其它地方。第三把LDAP存储和复制功能结合起来,可以定制目录树的结构以降低对WAN带宽的要求。位于西雅图的营销办公室需要每分钟更新的美国销售状况的信息,但是欧洲的销售情况就只要每小时更新一次就行了。刨根问底:基准DNLDAP目录树的最顶部就是根,也就是所谓的“基准DN"。基准DN通常使用下面列出的三种格式之一。假定我在名为FooBar的电子商务公司工作,这家公司在Internet上的名字是foobar.com。o="FooBar,Inc.",c=US(以X.500格式表示的基准DN)在这个例子中,o=FooBar,Inc.表示组织名,在这里就是公司名的同义词。c=US 表示公司的总部在美国。以前,一般都用这种方式来表示基准DN。但是事物总是在不断变化的,现在所有的公司都已经(或计划)上Internet上。随着Internet的全球化,在基准DN中使用国家代码很容易让人产生混淆。现在,X.500格式发展成下面列出的两种格式。o=foobar.com(用公司的Internet地址表示的基准DN)这种格式很直观,用公司的域名作为基准DN。这也是现在最常用的格式。dc=foobar,dc=com(用DNS域名的不同部分组成的基准DN)就象上面那一种格式,这种格式也是以DNS域名为基础的,但是上面那种格式不改变域名(也就更易读),而这种格式把域名:foobar.com分成两部分dc=foobar,dc=com。在理论上,这种格式可能会更灵活一点,但是对于最终用户来说也更难记忆一点。考虑一下foobar.com这个例子。当foobar.com和gizmo.com合并之后,可以简单的把“dc=com"当作基准DN。把新的记录放到已经存在的dc=gizmo,dc=com目录下,这样就简化了很多工作(当然,如果foobar.com和wocket.edu合并,这个方法就不能用了)。如果LDAP服务器是新安装的,我建议你使用这种格式。再请注意一下,如果你打算使用活动目录(ActriveDirectory),Microsoft已经限制你必须使用这种格式。更上一层楼:在目录树中怎么组织数据在UNIX文件系统中,最顶层是根目录(root)。在根目录的下面有很多的文件和目录。象上面介绍的那样,LDAP目录也是用同样的方法组织起来的。在根目录下,要把数据从逻辑上区分开。因为历史上(X.500)的原因,大多数LDAP目录用OU从逻辑上把数据分开来。OU表示“OrganizationUnit",在X.500协议中是用来表示公司内部的机构:销售部、财务部,等等。现在LDAP还保留ou=这样的命名规则,但是扩展了分类的范围,可以分类为:ou=people,ou=groups,ou=devices,等等。更低一级的OU有时用来做更细的归类。例如:LDAP目录树(不包括单独的记录)可能会是这样的:dc=foobar,dc=comou=customersou=asiaou=europeou=usaou=employeesou=roomsou=groupsou=assets-mgmtou=nisgroupsou=recipes单独的LDAP记录DN是LDAP记录项的名字在LDAP目录中的所有记录项都有一个唯一的“Distinguished Name",也就是DN。每一个LDAP记录项的DN是由两个部分组成的:相对DN(RDN)和记录在LDAP目录中的位置。RDN是DN中与目录树的结构无关的部。在LDAP目录中存储的记录项都要有一个名字,这个名字通常存在cn(CommonName)这个属性里。因为几乎所有的东西都有一个名字,在LDAP中存储的对象都用它们的cn值作为RDN的基础。如果我把最喜欢的吃燕麦粥食谱存为一个记录,我就会用cn=OatmealDeluxe作为记录项的RDN。1我的LDAP目录的基准DN是dc=foobar,dc=com2我把自己的食谱作为LDAP的记录项存在ou=recipes3我的LDAP记录项的RDN设为cn=OatmealDeluxe上面这些构成了燕麦粥食谱的LDAP记录的完整DN。记住,DN的读法和DNS主机名类似。下面就是完整的DN:cn=OatmealDeluxe,ou=recipes,dc=foobar,dc=com举一个实际的例子来说明DN现在为公司的员工设置一个DN。可以用基于cn或uid(UserID),作为典型的用户帐号。例如,FooBar的员工FranSmith(登录名:fsmith)的DN可以为下面两种格式:uid=fsmith,ou=employees,dc=foobar,dc=com(基于登录名)LDAP(以及X.500)用uid表示“UserID",不要把它和UNIX的uid号混淆了。大多数公司都会给每一个员工唯一的登录名,因此用这个办法可以很好地保存员工的信息。你不用担心以后还会有一个叫FranSmith的加入公司,如果Fran改变了她的名字(结婚?离婚?或宗教原因?),也用不着改变LDAP记录项的DN。cn=FranSmith,ou=employees,dc=foobar,dc=com(基于姓名)可以看到这种格式使用了CommonName(CN)。可以把CommonName当成一个人的全名。这种格式有一个很明显的缺点就是:如果名字改变了,LDAP的记录就要从一个DN转移到另一个DN。但是,我们应该尽可能地避免改变一个记录项的DN。定制目录的对象类型你可以用LDAP存储各种类型的数据对象,只要这些对象可以用属性来表示,下面这些是可以在LDAP中存储的一些信息:第一员工信息:员工的姓名、登录名、口令、员工号、他的经理的登录名,邮件服务器,等等。第二物品跟踪信息:计算机名、IP地址、标签、型号、所在位置,等等。第三客户联系列表:客户的公司名、主要联系人的电话、传真和电子邮件,等等。第四会议厅信息:会议厅的名字、位置、可以坐多少人、电话号码、是否有投影机。 第五食谱信息:菜的名字、配料、烹调方法以及准备方法。因为LDAP目录可以定制成存储任何文本或二进制数据,到底存什么要由你自己决定。LDAP目录用对象类型(objectclasses)的概念来定义运行哪一类的对象使用什么属性。在几乎所有的LDAP服务器中,你都要根据自己的需要扩展基本的LDAP目录的功能,创建新的对象类型或者扩展现存的对象类型。LDAP目录以一系列“属性对”的形式来存储记录项,每一个记录项包括属性类型和属性值(这与关系型数据库用行和列来存取数据有根本的不同)。下面是我存在LDAP目录中的一部分食谱记录:dn:cn=OatmealDeluxe,ou=recipes,dc=foobar,dc=comcn:InstantOatmealDeluxerecipeCuisine:breakfastrecipeIngredient:1packetinstantoatmealrecipeIngredient:1cupwaterrecipeIngredient:1pinchsaltrecipeIngredient:1tspbrownsugarrecipeIngredient:1/4apple,anytype请注意上面每一种配料都作为属性recipeIngredient值。LDAP目录被设计成象上面那样为一个属性保存多个值的,而不是在每一个属性的后面用逗号把一系列值分开。因为用这样的方式存储数据,所以数据库就有很大的灵活性,不必为加入一些新的数据就重新创建表和索引。更重要的是,LDAP目录不必花费内存或硬盘空间处理“空”域,也就是说,实际上不使用可选择的域也不会花费你任何资源。作为例子的一个单独的数据项让我们看看下面这个例子。我们用Foobar,Inc.的员工FranSmith的LDAP记录。这个记录项的格式是LDIF,用来导入和导出LDAP目录的记录项。dn:uid=fsmith,ou=employees,dc=foobar,dc=comobjectclass:personobjectclass:organizationalPersonobjectclass:inetOrgPersonobjectclass:foobarPersonuid:fsmithgivenname:Fransn:Smithcn:FranSmithcn:FrancesSmith telephonenumber:510-555-1234roomnumber:122Go:Foobar,Inc.mailRoutingAddress:fsmith@foobar.commailhost:mail.foobar.comuserpassword:{crypt}3x1231v76T89Nuidnumber:1234gidnumber:1200homedirectory:/home/fsmithloginshell:/usr/local/bin/bash属性的值在保存的时候是保留大小写的,但是在默认情况下搜索的时候是不区分大小写的。某些特殊的属性(例如,password)在搜索的时候需要区分大小写。让我们一点一点地分析上面的记录项。dn:uid=fsmith,ou=employees,dc=foobar,dc=com这是Fran的LDAP记录项的完整DN,包括在目录树中的完整路径。LDAP(和X.500)使用uid(UserID),不要把它和UNIX的uid号混淆了。objectclass:personobjectclass:organizationalPersonobjectclass:inetOrgPersonobjectclass:foobarPerson可以为任何一个对象根据需要分配多个对象类型。person对象类型要求cn(commonname)和sn(surname)这两个域不能为空。persion对象类型允许有其它的可选域,包括givenname、telephonenumber,等等。organizationalPerson给person加入更多的可选域,inetOrgPerson又加入更多的可选域(包括电子邮件信息)。最后,foobarPerson是为Foobar定制的对象类型,加入了很多定制的属性。uid:fsmithgivenname:Fransn:Smithcn:FranSmithcn:FrancesSmithtelephonenumber:510-555-1234roomnumber:122Go:Foobar,Inc.以前说过了,uid表示UserID。当看到uid的时候,就在脑袋里想一想 “login"。请注意CN有多个值。就象上面介绍的,LDAP允许某些属性有多个值。为什么允许有多个值呢?假定你在用公司的LDAP服务器查找Fran的电话号码。你可能只知道她的名字叫Fran,但是对人力资源处的人来说她的正式名字叫做Frances。因为保存了她的两个名字,所以用任何一个名字检索都可以找到Fran的电话号码、电子邮件和办公房间号,等等。mailRoutingAddress:fsmith@foobar.commailhost:mail.foobar.com就象现在大多数的公司都上网了,Foobar用Sendmail发送邮件和处理外部邮件路由信息。Foobar把所有用户的邮件信息都存在LDAP中。最新版本的Sendmail支持这项功能。Userpassword:{crypt}3x1231v76T89Nuidnumber:1234gidnumber:1200gecos:FrancesSmithhomedirectory:/home/fsmithloginshell:/usr/local/bin/bash注意,Foobar的系统管理员把所有用户的口令映射信息也都存在LDAP中。FoobarPerson类型的对象具有这种能力。再注意一下,用户口令是用UNIX的口令加密格式存储的。UNIX的uid在这里为uidnumber。提醒你一下,关于如何在LDAP中保存NIS信息,有完整的一份RFC。在以后的文章中我会谈一谈NIS的集成。第八节LDAP复制LDAP服务器可以使用基于“推”或者“拉”的技术,用简单或基于安全证书的安全验证,复制一部分或者所有的数据。例如,Foobar有一个“公用的”LDAP服务器,地址为ldap.foobar.com,端口为389。NetscapeCommunicator的电子邮件查询功能、UNIX的“ph"命令要用到这个服务器,用户也可以在任何地方查询这个服务器上的员工和客户联系信息。公司的主LDAP服务器运行在相同的计算机上,不过端口号是1389。你可能即不想让员工查询资产管理或食谱的信息,又不想让信息技术人员看到整个公司的LDAP目录。为了解决这个问题,Foobar有选择地把子目录树从主LDAP服务器复制到“公用”LDAP服务器上,不复制需要隐藏的信息。为了保持数据始终是最新的,主目录服务器被设置成即时“推”同步。这些种方法主要是为了方便,而不是安全,因为如果有权限的用户想查询所有的数据,可以用另一个LDAP端口。假定Foobar通过从奥克兰到欧洲的低带宽数据的连接用LDAP管理客户联系信息。可以建立从ldap.foobar.com:1389到munich-ldap.foobar.com:389的数据复制,象下面这样: periodicpull:ou=asia,ou=customers,o=sendmail.comperiodicpull:ou=us,ou=customers,o=sendmail.comimmediatepush:ou=europe,ou=customers,o=sendmail.com“拉”连接每15分钟同步一次,在上面假定的情况下足够了。“推”连接保证任何欧洲的联系信息发生了变化就立即被“推”到Munich。用上面的复制模式,用户为了访问数据需要连接到哪一台服务器呢?在Munich的用户可以简单地连接到本地服务器。如果他们改变了数据,本地的LDAP服务器就会把这些变化传到主LDAP服务器。然后,主LDAP服务器把这些变化“推”回本地的“公用”LDAP服务器保持数据的同步。这对本地的用户有很大的好处,因为所有的查询(大多数是读)都在本地的服务器上进行,速度非常快。当需要改变信息的时候,最终用户不需要重新配置客户端的软件,因为LDAP目录服务器为他们完成了所有的数据交换工作。第二章LDAP存储结构原理不少LDAP开发人员喜欢把LDAP与关系数据库相比,认为是另一种的存贮方式,然后在读性能上进行比较。实际上,这种对比的基础是错误的。LDAP和关系数据库是两种不同层次的概念,后者是存贮方式(同一层次如网格数据库,对象数据库)前者是存贮模式和访问协议。LDAP是一个比关系数据库抽象层次更高的存贮概念,与关系数据库的查询语言SQL属同一级别。LDAP是实现了指定的数据结构的存贮,它包括以下可以用关系数据库实现的结构要求:树状组织、条目认证、类型定义、许可树形记录拷贝。第一节树状组织无论是X.500还是LDAP都是采用树状方式进行记录。每一个树目录都有一个树根的入口条目,子记录全部是这一根条目的子孙。这是目录与关系数据类型最大的区别(关系数据库的应用结构也可实现树状记录)。因此,把目录看作是更高级的树状数据库也未尝不可,只不过除此外,它不能实现关系存贮的重要功能。第二节条目和条目认证LDAP是以条目作为认证的根据。ROOT的权限认证与目录本身无关,但除此外所有条目的认证权限由条目本身的密码进行认证。LDAP可以配置成各种各样不同的父子条目权限继承方式。每一个条目相当于一个单一的平面文本记录,由条目自身或指定的条目认证进行访问控制。因此,LDAP定义的存贮结构等同于一批树状组织的平面数据库,并提供相应的访问控制。条目中的记录以名-值对的形式存在,每一个名值对必须由数据样式schema预定义。因此,LDAP可以看作是以规定的值类型以名值对形式存贮在一系列以树状组织的平面数据库的记录的集合。第三节数据样式(schema) 数据样式schema是针对不同的应用,由用户指定(设计)类和属性类型预定义,条目中的类(objectclass)和属性必须在在LDAP服务器启动时载入内存的schema已有定义。因此,AD活动目录中的条目记录就必须符合ActiveDirectory的schema中。如果已提供的schema中的定义不够用,用户可以自行定义新的schema。在这里中可以看到常用的schema。第四节类型分类(objectClass)条目中的记录通过objectclass实现分类,objectClass是一个继承性的类定义,每一个类定义指定必须具备的属性。如某一条目指定必须符合unit类型,则它必须具备chinacfirm类形指定的属性,象法人代表什么的。通过objectclass分类,分散的条目中的记录就实际上建立了一个索引结构,为高速的读查询打下了基础。Objectclass也是过滤器的主要查询对象。第五节过滤器和语法LDAP是一个查询为主的记录结构,无论是何种查询方式,最终都由过滤器缺点查询的条件。过滤器相当于SQL中的WHERE子句。任何LDAP的类过滤和字符串都必须放在括号内,如(objectclass=*),指列出所有类型的记录(不过分类)。可以使用=,>=,<=,~=(约等于)进行比较,如(number<=100)。合并条件是最怪的,必须把操作符放在两个操作对象的前面而不是中间,单一操作对象用括号括起来。如A与B,不是A&B,而是(&(A)(B))。或使用"|"表示;非使用"!"表示。对于"与",或"或"在操作符后可以跟多个条件表达式,但非后则只参是单个表达式。详见RFC1558。第六节树移植LDAP最重要的特性和要求并不是读性能,而是扩展性。这一特性是通过树移植和树复制实现的。按LDAP的RFC要求,LDAP目录应该可以任意地在不同的目录间连接、合并并实现自动复制,及自动性同步。这意味着用户可以在任一LDAP中访问条目,而不用管其中某一部分是否复制自全世界另一目录中的记录,同时另一目录中的记录同样在正常运作。这一特性如果在关系数据库中实现,意味着要使用程序化的非规范化预复制。类似于汇总帐目的设计。第七节LDIF交换文件LDIF是LDAP约定的记录交换格式,以平面文本的形式存在,是大部分LDAP内容交换的基础,如拷贝、添加、修改等操作,都是基于LDIF文件进行操作。第八节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()获得该对象的句柄,然后强制造型成所需要的对象就可以了,如:Personp=(Person)contex.lookup("cn=elvis,dc=daifu,dc=com");这个句法在EJB的程序中,是经常用到的。使用CORBA的跨语言性质,使用CORBA存储对象比JAVA更加诱人,这意味着所存储的对象可以被任何语言编写的客户端访问。其实,微软的.net说到底也非常简单,无非是把COM对象存储到微软自家的目录ActiveDirectory里面,从而可以在网络范围内使用任何微软平台的语言进行对象访问而已。众所周知,COM就是与CORBA相对的微软规范。使用对象串行化技术,可以把常用对象如某个打印机,某个客户直接存储到LDAP中,然后快速获取该对象的引用,这样,就比把对象信息存储到关系数据库中,分别取出属性,然后再初始化对象操作的做法,效率要高得多了。这是LDAP目前比普通关系数据库存储要优秀的地方,而对象数据库还不成熟。第三章LDAP目录客户端访问工具第一节openldap命令行Openldap提供了在UNIX命令行下的访问工具集。包括ldapsearch,ldapadd,ldapmodify,ldappassword,ldapdelete等必要的工具。除了使用man获得使用帮助外,还可以在http://www.tldp.org/HOWTO/LDAP-HOWTO/,及http://www.csis.gvsu.edu/GeneralInfo/Oracle/network.920/a96579/,获得使用的支持。例子:通过查询根上下文判断LDAP服务器是否正常工作:$ldapsearch-x-b""-sbase"objectclass=*"namingContexts注:该命令查询该当前服务器上的命令上下文,通常就是rootdn的上下文记录。-x指该查询使用目录认证而不是使用SASL认证,;-b“”是查询的起点,即base,空指从根开始查询;-sbase指查询范围。有三种选项,one指一层,包括兄弟条目;base指当前条目,sub,子孙记录。默认是sub.“objectclass=*”是过滤器,表示所有记录类型都加以选择;namingContexts是约定的特殊属性,可以选择其他属性值进行查询。$ldapsearch-x-b"dc=daifu,dc=com"-sbase"objectclass=organization"dndc注:-b“dc=daifu,dc=com”指查询的是“dc=daifu,dc=com”的条目,需要注意的是,slapd.conf中指定rootdn为“dc=daifu,dc=com”,并不等同于目录中已经具有真实的“dc=daifu,dc=com”条目。"objectclass=organization"指查询条件是organization类的。“dcdc”指只需列出dn,dc两项属性。 (ldapadd),ldapmodify的操作是基于LDIF文件的,所以必须先按规则生成LDIF文本文件,然后执行导入。要注意的是,新装的LDAP具备一个上下文,并不等于在目录中有相应的条目。如,OPENLDAP的slapd.conf中已经定义了一个根“dc=daifu,dc=com”,并不等于可以把新的条目添加到”dc=daifu,dc=com”,因此实际上并没有这一条目,必须先执行添加相应的条目,然后才可以添加后续条目。其次,LDIF的格式文件非常严格,空间被认为是确定的字符,因此,需要特别注意每行后面不应带有空格。第二节ldapbrowserJAVA开源LDAP客户端工具ldapbrowser是开源的LDAP浏览工具,并带有不太强的条目编辑功能。Ldapbrowser是纯JAVA的程序,可跨平台运行,在开源的程序中,是最优秀的一个。缺点是不能浏览服务器端的schema对象,从而限制了条目编辑(添加)的能力。其次,由于JAVA对LDAP的访问方式在添加时,必须预先生成一个实现DirContext接口的类,因此,这也令订制型的添加操作在JAVA实现时相当困难。第三节ldapadministratorldapadministrator是一个windows的收费程序,试用一个月。Ldapadministrator除了具备ldapbrowser的功能外,在条目编辑上的功能大为增强。但从另一个角度看,LDAP总是涉及到大量的条目,当需要编辑的条目急速增加时,使用ldapadminstrator就不是轻松的事情,此时还是使用LDIF文件交换为佳。第四节浏览器根据rfc2255.txt的约定,可以使用URI定义LDAP查询,因此,理论上,只要浏览器内嵌支持,就可以作为LDAP客户端使用。IE浏览器支持简单的LDAP查询。此时,IE把LDAP的URI看作是查询的对象。但是IE以及Exchangeserver非常狭隘地把LDAP看作是纯粹为EMAIL地址查询服务的,只能以“图形”的方式显示查到的邮件地址什么的。因此,IE准确地说,是对LDAP存储的邮件地址信息的查询工具。URI查询语法是:ldapurl=scheme"://"[hostport]["/"[dn["?"[attributes]["?"[scope]["?"[filter]["?"extensions]]]]]]scheme="ldap"attributes=attrdesc*(","attrdesc)scope="base"/"one"/"sub"dn=distinguishedNamefromSection3of[1]hostport=hostportfromSection5ofRFC1738[5]attrdesc=AttributeDescriptionfromSection4.1.5of[2]filter=filterfromSection4of[4] extensions=extension*(","extension)extension=["!"]extype["="exvalue]extype=token/xtokenexvalue=LDAPStringfromsection4.1.2of[2]token=oidfromsection4.1of[3]xtoken=("X-"/"x-")tokenbr/>例:类似#ldapsearch–x–h192.168.0.2–p389-b“dc=daifu,dc=com”–b“sub”“objectclass=qmailuser”URI查询是:ldap://192.168.0.2:389/dc=daifu,dc=com??sub?objectclass=qmailuser?浏览器并不是完全的LDAP客户工具。第五节ldapexplorer(PHP编写的LDAP客户端工具)一个用PHP写的LDAP处理工具第六节ADAMRedistx86chs.exe一个微软ActiveDirctoryServer专用的目录树工具第七节SunOneDirectoryConsole一个SunOneDirectoryServer带的目录树客户端工具第四章LDAP目录服务器第一节SunOneDirectoryServer第二节openLdapDirectoryServer第三节WindowsActiveDirectoryServer第五章LDAP程序访问技术详解第一节Sun公司的JNDI与各厂商实现的LDAP函数库关系Sun公司的JNDI是一个规范LDAP操作的接口,其具体实现如下面的厂商提供,使用JNDI访问LDAP目录服务,可以实现不依赖具体厂商的LDAP函数库。1Novell函数库:Novel提供了基于普通LDAP函数库的扩展,主要包括两个部分:针对NoveleDirectory服务器产品的扩展,其次是对如ldapsearch等常用函数的扩展。详情可从:http://developer.novell.com/ndk/qstart/opensource.htm#ldapc获得帮助;2Netscape函数库:Netscape一度是企业级目录服务提供者,许多LDAP的C例子,实际上都是基于Netscape服务器的。但在Netscape被收购后,其目录服务成了iPlanet和SUNeDirectory产品的一部分,出于支持JAVA和iplanet产品的缘故,SUN对该产品和相关库的支持远不够积极,特别是对linux的支持不够充分,估计也与保护solaris产品有关。3Mozilla函数库: Mozilla可以看作是Netscape的另一个分支。准确地说,Netscape本来就是源于Mozilla。Mozilla是也是一个开源的项目,提供完整的C-SDK,缺点是对linux的支持不够充分。第二节访问LDAP数据的API-JNDI1J2SE和JNDI关系Java2SDK1.3及以上的版本包含了JNDI。对于JDK1.1和1.2也有一个标准的扩展。Java2SDK1.4.x的最新版本包括了几个增强和下面的命名/目录服务提供者:1、LDAP(LightweightDirectoryAccessProtocol)服务提供者2、CORBACOS(CommonObjectRequestBrokerArchitectureCommonObjectServices)命名服务提供者3、RMI(JavaRemoteMethodInvocation)注册服务提供者4、DNS(DomainNameSystem)服务提供者2JNDIAPIJNDIAPI由5个包组成:1、Javax.naming:包含了访问命名服务的类和接口。例如,它定义了Context接口,这是命名服务执行查询的入口。2、Javax.naming.directory:对命名包的扩充,提供了访问目录服务的类和接口。例如,它为属性增加了新的类,提供了表示目录上下文的DirContext接口,定义了检查和更新目录对象的属性的方法。3、Javax.naming.event:提供了对访问命名和目录服务时的时间通知的支持。例如,定义了NamingEvent类,这个类用来表示命名/目录服务产生的事件,定义了侦听NamingEvents的NamingListener接口。4、Javax.naming.ldap:这个包提供了对LDAP版本3扩充的操作和控制的支持,通用包javax.naming.directory没有包含这些操作和控制。5、Javax.naming.spi:这个包提供了一个方法,通过javax.naming和有关包动态增加对访问命名和目录服务的支持。这个包是为有兴趣创建服务提供者的开发者提供的。3JNDI类库下载下载类库jndi.jar下载网址:http://java.sun.com/jndi/4JNDI对目录服务的一些基本操作i目录服务器建立连接(匿名访问方式)程序段:/*实例化一个Hashtable,用于存储连接属性*/Hashtableenv=newHashtable(5,0.75f);/*设置连接Ldap的实现工厂*/env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); /*设置连接URL*/env.put(Context.PROVIDER_URL,“ldap://localhost:389”);/*实例化一个目录上下文*/LdapContextctx=newInitialLdapContext(env,null);ii目录服务器建立连接(简单验证方式Simple)程序段:/*实例化一个Hashtable,用于存储连接属性*/Hashtableenv=newHashtable(5,0.75f);/*设置连接Ldap的实现工厂*/env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");/*设置连接URL*/env.put(Context.PROVIDER_URL,“ldap://localhost:389”);/*设置验证方式为简单验证simple*/env.put(Context.SECURITY_AUTHENTICATION,"simple");/*进入的目录识别名DNEnv.MGR_DN为进入的目录识别名*/env.put(Context.SECURITY_PRINCIPAL,Env.MGR_DN);/*进入的目录密码passwordEnv.MGR_PW为进入的目录密码*/env.put(Context.SECURITY_CREDENTIALS,Env.MGR_PW);/*实例化一个目录上下文*/LdapContextctx=newInitialLdapContext(env,null);iii获取目录服务器指定条目的属性值/*定义需获取的属性名*/String[]attrs=newString[5];attrs[0]="cn";/*获取属性”cn”*/attrs[1]="sn";/*获取属性”sn”*/attrs[2]="mail";/*获取属性”mail”*/attrs[3]="telephonenumber";/*获取属性”telephonenumber”*/attrs[4]="title";/*获取属性”title”*//*获取指定条目*/Attributesresult=ctx.getAttributes(Env.ENTRYDN,attrs);/*获取指定属性*/Attributeattr=result.get("cn");按条目属性值的搜索条件搜索目录服务器上的条目/*实例化一个搜索器*/ SearchControlsconstraints=newSearchControls();/*设置搜索器的搜索范围*/constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);/*在基目录中搜索条件为Env.MY_FILTER的所有属性*/Env.MY_SEARCHBASE为搜索的基目录Env.MY_FILTER为搜索的过渡器注意:这里返回是的所有的条目集合*/NamingEnumerationresults=ctx.search(Env.MY_SEARCHBASE,Env.MY_FILTER,constraints);/*打印条目的识别名(DN)及其所有的属性名,值*/while(results!=null&&results.hasMore()){/*取一个条目*/SearchResultsi=(SearchResult)results.next();/*打印条目识别名(DN)*/System.out.println("name:"+si.getName());/*获取条目的所有属性集合*/Attributesattrs=si.getAttributes();if(attrs==null){System.out.println("Noattributes");}else{/*打印所有属性值*/for(NamingEnumerationae=attrs.getAll();ae.hasMoreElements();){/*获取一个属性*/Attributeattr=(Attribute)ae.next();StringattrId=attr.getID();/*打印这个属性的所有属性值*/for(Enumerationvals=attr.getAll();vals.hasMoreElements();System.out.println(attrId+":"+vals.nextElement()));}}iv重命名目录服务器上的条目识别名(DN)程序:try{/*将条目dn,更名为nrdn*/ ctx.rename(dn,nrdn);System.out.println("Themodrdnoperationwassuccessful.Entry"+dn+"hasbeenchangedto"+nrdn+".");}catch(NamingExceptione){/*操作失败,则触发此意外*/System.err.println("Modifyoperationfailed."+e);}}v删除,添加,修改目录服务器指定条目的属性值程序:/*实例化一个用于修改属性的数组*/ModificationItem[]mods=newModificationItem[3];/*实例化一个BasicAttribute基本属性对象其第一个参数为属性名,第二个参数为属性值*/Attributemod0=newBasicAttribute("mail","babs@eng");//定义一个修改的属性条mods[0]=newModificationItem(DirContext.REPLACE_ATTRIBUTE,mod0);/*实例化一个BasicAttribute基本属性对象其第一个参数为属性名,第二个参数为属性值*/Attributemod1=newBasicAttribute("description","ThisentrywasmodifiedwiththeModattrsprogramon"+(newDate()).toString());//定义一个增加的属性条mods[1]=newModificationItem(DirContext.ADD_ATTRIBUTE,mod1);/*实例化一个BasicAttribute基本属性对象其第一个参数为属性名/Attributemod2=newBasicAttribute("description");//定义一个删除的属性条mods[2]=newModificationItem(DirContext.REMOVE_ATTRIBUTE,mod2);/*执行操作*/ctx.modifyAttributes(Env.ENTRYDN,mods);删除,添加目录服务器上的条目/*建一个我们将要增加的条目的属性*/AttributeobjClasses=newBasicAttribute("objectclass");/*以下给属性增加属性值 注意:这是一个属性对应多个属性值*/objClasses.add("top");/*给属性objectclass增加属性值"top"*/objClasses.add("person");/*给属性objectclass增加属性值"person"*/objClasses.add("organizationalPerson");/*给属性objectclass增加属值"organizationalPerson"*/objClasses.add("inetOrgPerson");/*给属性objectclass增加属性值"inetOrgPerson"*//*v建立一个属性,其属性名为"cn",值为"JacquesSmith"*/Attributecn=newBasicAttribute("cn","JacquesSmith");/*v建立一个属性,其属性名为"sn",值为"Smith"*/Attributesn=newBasicAttribute("sn","Smith");/*v建立一个属性,其属性名为"givenname",值为"Jacques"*/AttributegivenNames=newBasicAttribute("givenname","Jacques");/*指定一个我们将要增加DN*/Stringdn="uid=liaowufeng,"+Env.MY_MODBASE;/*指定一个我们将要删除的DN*/Stringndn="uid=JacquesSmith,"+Env.MY_MODBASE;/*指定一个DN名,我们将用于给DN更名*/Stringnrdn="uid=zhengxianagyang";/*实例化一个Hashtable,用于存储连接属性*/Hashtableenv=newHashtable(5,0.75f);/*设置连接Ldap的实现工厂*/env.put(Context.INITIAL_CONTEXT_FACTORY,Env.INITCTX);/*设置连接URL*/env.put(Context.PROVIDER_URL,Env.MY_SERVICE);/*设置验证方式为简单验证simple*/env.put(Context.SECURITY_AUTHENTICATION,"simple");/*进入的目录识别名DNenv.put(Context.SECURITY_PRINCIPAL,Env.MGR_DN);/*进入的目录密码passwordenv.put(Context.SECURITY_CREDENTIALS,Env.MGR_PW);/*实例化一个目录上下文*/DirContextctx=null;try{/*/*实例化一个目录上下文*/*/ctx=newInitialDirContext(env);/*实例化一个属性集合*/Attributesorig=newBasicAttributes(); /*装入属性*/orig.put(objClasses);orig.put(cn);orig.put(sn);orig.put(givenNames);/*在识别名为DN的目录中增加一个条目*/ctx.createSubcontext(dn,orig);System.out.println("Addedentry"+dn+".");}catch(NameAlreadyBoundExceptione){/*如果条目存在,则触发此Exception*/System.out.println("Entry"+dn+"alreadyexists,noneedtoadd");}catch(NamingExceptione){System.err.println("Modrdn:problemaddingentry."+e);System.exit(1);}try{/*删除一个条目ndn*/ctx.destroySubcontext(ndn);System.out.println("Deletedentry"+ndn+".");}catch(NameNotFoundExceptione){/*若要删除的条目不存在,则触发此Exception*/System.out.println("Entry"+ndn+"isnotinthedirectory."+"Noneedtodelete.");}catch(NamingExceptione){System.err.println("Modrdn:problemdeletingentry."+e);System.exit(1);}/*更改条目DN(识别名)操作*/try{/*将条目dn,更名为nrdn*/ctx.rename(dn,nrdn);System.out.println("Themodrdnoperationwassuccessful.Entry"+dn+"hasbeenchangedto"+nrdn+".");}catch(NamingExceptione){/*操作失败,则触发此意外*/ System.err.println("Modifyoperationfailed."+e);}}vi封装JNDI操作LDAP服务器的工具类目标:使用者只需要会使用List,Map数据结构,将对LDAP的操作进行封装类:主要有三个类1Env类包含LDAP的连接信息2LdapConnectionFactory类ldap连接工厂,提供初始化及获取ldap连接的方法3LdapOperUtilsldap的处理工具类,提供了各种操作ldap的方法。连接LDAP的连接属性类packagecom.common.ldapconnection;importorg.apache.log4j.Logger;/***功能描述:连接LDAP的连接属性*@authorliaowufeng*@version1.0*/publicclassEnv{//调用log4j的日志,用于输出privateLoggerlog=Logger.getLogger(Env.class.getName());//无论用什么LDAP服务器的固定写法,指定了JNDI服务提供者中工厂类publicStringfactory;//服务连接地址publicStringurl;//登陆LDAP的用户名和密码publicStringadminUID;//登陆LDAP用户密码publicStringadminPWD;//安全访问需要的证书库publicStringsslTrustStore;//安全通道访问publicStringsecurityProtocol;//连接TimeOutpublicStringtimeOut;/***构造函数 */publicEnv(){}/***构造函数*@paramfactoryLDAP工厂类*@paramurlLDAPURL*@paramadminUIDLDAP用户*@paramadminPWDLDAP密码*/publicEnv(Stringfactory,Stringurl,StringadminUID,StringadminPWD){this.factory=factory;this.url=url;this.adminUID=adminUID;this.adminPWD=adminPWD;}/***构造函数*@paramfactoryLDAP工厂类名*@paramurlLDAPURL*@paramadminUIDLDAP用户*@paramadminPWDLDAP密码*@paramsslTrustStore安全访问需要的证书*@paramsecurityProtocol安全通道访问*/publicEnv(Stringfactory,Stringurl,StringadminUID,StringadminPWD,StringsslTrustStore,StringsecurityProtocol){this.factory=factory;this.url=url;this.adminUID=adminUID;this.adminPWD=adminPWD;this.sslTrustStore=sslTrustStore;this.securityProtocol=securityProtocol;}/***构造函数*@paramfactoryLDAP工厂类名 *@paramurlLDAPURL*@paramadminUIDLDAP用户*@paramadminPWDLDAP密码*@paramsslTrustStore安全访问需要的证书*@paramsecurityProtocol安全通道访问*/publicEnv(Stringfactory,Stringurl,StringadminUID,StringadminPWD,StringtimeOut,StringsslTrustStore,StringsecurityProtocol){this.factory=factory;this.url=url;this.adminUID=adminUID;this.adminPWD=adminPWD;this.timeOut=timeOut;this.sslTrustStore=sslTrustStore;this.securityProtocol=securityProtocol;}}ldap连接工厂,提供初始化ldap连接的方法packagecom.common.ldapconnection;importjava.util.Properties;importjavax.naming.Context;importjavax.naming.NamingException;importjavax.naming.directory.DirContext;importjavax.naming.directory.InitialDirContext;importorg.apache.log4j.Logger;importorg.apache.commons.lang.StringUtils;/***功能描述:ldap连接工厂,提供初始化ldap连接的方法。*@authorliaowufeng*@version1.0*/publicclassLdapConnectionFactory{//初始化日志处理类staticLoggerlog=Logger.getLogger(LdapConnectionFactory.class.getName());/** *构造函数私有,防止实例化*/privateLdapConnectionFactory(){}/***从LDAP连接池中取得一个连接*@parampoolNameString连接池名*@env连接LDAP的连接信息*@returnDirContext*/publicstaticDirContextgetDirContext(StringpoolName,Envenv){//取得Contexttry{//日志打印,表明进入此方法log.debug(Constants.INTOMETHOD+"getDirContext");//初始化Properties对象PropertiesmEnv=newProperties();//使用LDAP/AD的认证方式mEnv.put(Context.AUTHORITATIVE,"true");//使用连接池mEnv.put("com.sun.jndi.ldap.connect.pool","true");//设定LDAP/AD的连接工厂mEnv.put(Context.INITIAL_CONTEXT_FACTORY,env.factory);//设定LDAP/AD的url地址mEnv.put(Context.PROVIDER_URL,env.url);//设定连接TimeOutif(!StringUtils.isEmpty(env.timeOut)){mEnv.put("com.sun.jndi.ldap.connect.timeout",env.timeOut);}//设定安全模式为simple方式mEnv.put(Context.SECURITY_AUTHENTICATION,"simple");//ssl通道访问if(env!=null&&"ssl".equals(env.securityProtocol)){//设定访问协议为sslmEnv.put(Context.SECURITY_PROTOCOL,env.securityProtocol);//设置访问证书属性,若没有此证书将无法通过ssl访问ADSystem.setProperty("javax.net.ssl.trustStore",env.sslTrustStore); }//读取可以登陆ldap的帐号、密码mEnv.put(Context.SECURITY_PRINCIPAL,env.adminUID);mEnv.put(Context.SECURITY_CREDENTIALS,env.adminPWD);log.debug(Constants.INIT+"DirContext");//通过参数连接LDAP/ADDirContextctx=newInitialDirContext(mEnv);log.debug("LdapConnectionFactory:"+Constants.INIT+"DirContext"+Constants.SUCCESS);returnctx;}catch(NamingExceptionex){ex.printStackTrace();log.error("LdapConnectionFactory:"+Constants.INIT+"DirContext"+Constants.FAIL);log.error(ex.getMessage());returnnull;}}/***关闭LDAP连接*@paramdirContextDirContext已连接的LDAP的Context实例*/publicstaticvoidcloseDirContext(DirContextdirContext){try{if(dirContext!=null)dirContext.close();}catch(Exceptionex){ex.printStackTrace();log.error("notcloseDirContext");}}/***获取LDAP服务器连接的方法*@paramenv连接LDAP的连接信息*@returnDirContext-LDAPserver的连接 */publicstaticDirContextgetDirContext(Envenv){try{//初始化Properties对象PropertiesmEnv=newProperties();//使用LDAP/AD的认证方式mEnv.put(Context.AUTHORITATIVE,"true");//设定LDAP/AD的连接工厂mEnv.put(Context.INITIAL_CONTEXT_FACTORY,env.factory);//设定LDAP/AD的url地址mEnv.put(Context.PROVIDER_URL,env.url);//设定连接TimeOutif(!StringUtils.isEmpty(env.timeOut)){mEnv.put("com.sun.jndi.ldap.connect.timeout",env.timeOut);}//设定安全模式为simple方式mEnv.put(Context.SECURITY_AUTHENTICATION,"simple");//ssl通道访问if(env!=null&&"ssl".equals(env.securityProtocol)){//设定访问协议为sslmEnv.put(Context.SECURITY_PROTOCOL,env.securityProtocol);//设置访问证书属性,若没有此证书将无法通过ssl访问ADSystem.setProperty("javax.net.ssl.trustStore",env.sslTrustStore);}//读取可以登陆ldap的帐号、密码mEnv.put(Context.SECURITY_PRINCIPAL,env.adminUID);mEnv.put(Context.SECURITY_CREDENTIALS,env.adminPWD);log.debug(Constants.INIT+"DirContext");//通过参数连接LDAP/ADDirContextctx=newInitialDirContext(mEnv);log.debug("LdapConnectionFactory:"+Constants.INIT+"DirContext"+Constants.SUCCESS);returnctx;}catch(NamingExceptionex){ex.printStackTrace();log.error("LdapConnectionFactory:"+Constants.INIT+"DirContext"+ Constants.FAIL);log.error(ex.getMessage());returnnull;}}}ldap的处理工具类,提供了各种操作ldap的方法。packagecom.common.ldapconnection;importjava.util.List;importjava.util.Vector;/****功能描述:ldap的处理类,提供了各种操作ldap的方法。*@authorliaowufeng*@version1.0*/publicclassLdapOperUtils{//调用log4j的日志,用于输出privatestaticLoggerlog=Logger.getLogger(LdapOperUtils.class.getName());/***根据连接Env信息,取得LdapDirContext*@paramenv连接Env的连接信息*@returnLdap连接的DirContext*@throwsBaseException*/privatestaticDirContextgetLdapDirContext(Envenv)throwsBaseException{//参数为空if(env==null){String[]args={"env"};//打印错误日志StringBuffermsglog=newStringBuffer("emptyinvokeparameterenvNULL");log.error(msglog.toString());thrownewBaseException("error.common.parameter.empty",args);}//定义DirContext DirContextdirContext=null;//从Ldap连接工厂中,取得Ldap连接dirContext=LdapConnectionFactory.getDirContext(env);returndirContext;}/***根据在ldappool.properties文件定义的LdapDirContext池名,取得LdapDirContext*@paramdirContextNameLdapDirContext池名*@return返回操作Ldap的DirContext*@throwsBaseException*/privatestaticDirContextgetLdapDirContext(StringdirContextName,Envenv)throwsBaseException{//参数为空if(StringUtils.isEmpty(dirContextName)){String[]args={"dirContextName"};//打印错误日志StringBuffermsglog=newStringBuffer("emptyinvokeparameterdirContextNameNULL");log.error(msglog.toString());thrownewBaseException("error.common.parameter.empty",args);}//定义DirContextDirContextdirContext=null;br/>//从Ldap连接工厂中,取得Ldap连接dirContext=LdapConnectionFactory.getDirContext(dirContextName,env);returndirContext;}/***关闭LDAP连接*@paramdirContextDirContext*@throwsBaseException*/publicstaticvoidcloseEnvLdapDirContext(DirContextdirContext)throwsBaseException{//关闭LDAP连接closeLdapDirContext(dirContext); }/***关闭Ldap的DirContext*@paramdirContext连接Ldap的DirContext*@throwsBaseException*/privatestaticvoidcloseLdapDirContext(DirContextdirContext)throwsBaseException{//如果参数为NULLif(dirContext==null){String[]args={"dirContext"};//打印错误日志StringBuffermsglog=newStringBuffer("emptyinvokeparameterconnNULL");log.error(msglog.toString());thrownewBaseException("error.common.parameter.empty",args);}try{//关闭dirContext.close();}catch(NamingExceptionex){//关闭不成功,再次关闭if(log.isDebugEnabled()){log.debug("NotcloseDirContext"+ex);}//记录日志log.error("NotcloseDirContext"+ex);ex.printStackTrace();try{//再次关闭dirContext.close();}catch(NamingExceptionex1){//再次关闭失败if(log.isDebugEnabled()){log.debug("NotagaincloseDirContext"+ex);}//记录日志 log.error("NotagaincloseDirContext"+ex);ex.printStackTrace();//抛出异常thrownewBaseException("error.common.dao.ldap.closedircontext",null);}}}/***构造函数私有,防止实例化*/privateLdapOperUtils(){}/***在当前的Context添加一个子Context*@paramcontext连接DirContext*@paramcn创建的子Context*@paramattMapContext的属性,Map包含List,key=属性名,*当属性值为多值时,为list,为单值时,为String*@throwsNamingException*@throwsBaseException*/publicstaticvoidaddContext(DirContextcontext,Stringcn,MapattMap)throwsNamingException,BaseException{//参数为空if(context==null){String[]args={"context"};//打印错误日志StringBuffermsglog=newStringBuffer("emptyinvokeparametercontextNULL");log.error(msglog.toString());thrownewBaseException("error.common.parameter.empty",args);}//参数为空if(StringUtils.isEmpty(cn)){String[]args={ "cn"};//打印错误日志StringBuffermsglog=newStringBuffer("emptyinvokeparametercnNULL");log.error(msglog.toString());thrownewBaseException("error.common.parameter.empty",args);}//参数为空if(attMap==null){String[]args={"attMap"};//打印错误日志StringBuffermsglog=newStringBuffer("emptyinvokeparameterattMapNULL");log.error(msglog.toString());thrownewBaseException("error.common.parameter.empty",args);}//为空,则退出if(attMap.isEmpty()){return;}//取所有的属性keySetkeySet=attMap.keySet();IteratorkeyIterator=keySet.iterator();Attributesattrs=newBasicAttributes();//迭代所有的属性keywhile(keyIterator.hasNext()){//取下一个属性Stringkey=(String)keyIterator.next();Attributeatt=null;ObjectvalueObj=attMap.get(key);//判断属性类型if(valueObjinstanceofString){//为字符串,为单值属性att=newBasicAttribute(key,valueObj);}elseif(valueObjinstanceofList){//为List,为多值属性att=newBasicAttribute(key); ListvalueList=(List)valueObj;//加入多值属性for(inti=0;i

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭