LDAP学习笔记

 
LDAP学习笔记 2007-10-16 10:30

本文系作者原创,转载请保留出处:http://marion.cublog.cn
其中理解可能也有不当之处,欢迎各位指正。

第一部分:openldap介绍

一、Directory Services(目录服务)能做什么?

     我们知道,当局域网的规模变的越来越大时,为了方便主机管理,我们使用DHCP来实现IP地址、以太网地址、主机名和拓扑结构等的集中管理和统一分配。同样,如果一个局域网内有许多的其它资源时,如打印机、共享文件夹等等,为了方便的定位及查找它们,一种集中定位管理的方式或许是较好的选择,DNS和NIS都是用来实现类似管理的方法。
    
     对于局域网内的一个用户来讲,工作等其它应用需要,我们必须凭帐号登录主机、用帐号收发E-mail,甚至为了管理需要公司还需要维护一个电子号码簿来存储员工的姓名、地址、电话号码等信息。随着时间的增长,我们会为这些越来越多的帐号和密码弄的头晕脑胀。同时,如果一个员工离开,管理员就不得不翻遍所有的记录帐号信息的文件把离职员工的信息删除。这些将是一个繁琐而效率低下的工作。那么,如果能将此些帐号信息等统一到一个文件中进行管理,无疑会大大提高员工及管理员的工作效率。目录服务(LDAP是其实现的一种)正是基于这些应用实现的。

二、什么是LDAP?
    
     LDAP是Lightweight Directory Access Protocol的缩写,顾名思义,它是指轻量级目录访问协议(这个主要是相对另一目录访问协议X.500而言的;LDAP略去了x.500中许多不太常用的功能,且以TCP/IP协议为基础)。目录服务和数据库很类似,但又有着很大的不同之处。数据库设计为方便读写,但目录服务专门进行了读优化的设计,因此不太适合于经常有写操作的数据存储。同时,LDAP只是一个协议,它没有涉及到如何存储这些信息,因此还需要一个后端数据库组件来实现。这些后端可以是bdb(BerkeleyDB)、ldbm、shell和passwd等。

     LDAP目录以树状的层次结构来存储数据(这很类同于DNS),最顶层即根部称作“基准DN”,形如"dc=mydomain,dc=org"或者"o=mydomain.org",前一种方式更为灵活也是Windows AD中使用的方式。在根目录的下面有很多的文件和目录,为了把这些大量的数据从逻辑上分开,LDAP像其它的目录服务协议一样使用OU(Organization Unit),可以用来表示公司内部机构,如部门等,也可以用来表示设备、人员等。同时OU还可以有子OU,用来表示更为细致的分类。

     LDAP中每一条记录都有一个唯一的区别于其它记录的名字DN(Distinguished Name),其处在“叶子”位置的部分称作RDN;如dn:cn=tom,ou=animals,dc=mydomain,dc=org中tom即为RDN;RDN在一个OU中必须是唯一的。

三、什么是LDIF?

     LDIF(LDAP Interchange Format)是指存储LDAP配置信息及目录内容的标准文本文件格式,之所以使用文本文件来格式来存储这些信息是为了方便读取和修改,这也是其它大多数服务配置文件所采取的格式。LDIF文件常用来向目录导入或更改记录信息,这些信息需要按照LDAP中schema的格式进行组织,并会接受schema的检查,如果不符合其要求的格式将会出现报错信息。LDIF文件样例如下:

#LDIF file example
dn: dc=mydomain,dc=org
objectClass: domain
dc: mydomain

     其中,以“#”号开头的为注释行;第二行起的行中,冒号左边为属性,右边是属性的值,这类同于编程中的变量及为其所赋的值,但属性可以被重复赋值。

四、objectClass

     LDAP中,一条记录必须包含一个objectClass属性,且其需要赋予至少一个值。每一个值将用作一条LDAP记录进行数据存储的模板;模板中包含了一条记录中数个必须被赋值的属性和一系列可选的属性。如上述LDIF文件中的记录所示,objectClass的值为domain。

     objectClass有着严格的等级之分,最顶层的类是top和alias。例如,organizationalPerson这个objectClass隶属于Person,而Person又是top的子类。

     objectClass大致分为三类:结构型的(如:person和organizationUnit)、辅助型的(如:extensibeObject)和抽象型的(这类不能直接使用)。官方定义的objectClass,如下所示:

alias
applicationEntity
dSA
applicationProcess
bootableDevice
certificationAuthority
certificationAuthority-V2
country
cRLDistributionPoint
dcObject
device
dmd
domain
domainNameForm
extensibleObject
groupOfNames
groupOfUniqueNames
ieee802Device
ipHost
ipNetwork
ipProtocol
ipService
locality
dcLocalityNameForm
nisMap
nisNetgroup
nisObject
oncRpc
organization
dcOrganizationNameForm
organizationalRole
organizationalUnit
dcOrganizationalUnitNameForm
person
organizationalPerson
inetOrgPerson
uidOrganizationalPersonNameForm
residentialPerson
posixAccount
posixGroup
shadowAccount
strongAuthenticationUser
uidObject
userSecurityInformation

五、Attribute介绍

     如上文所述,Attribute类同于编程语言中的变量,它可以被赋值,就像是可以存放一个单一类型信息的容器。官方声明了许多常用的Attribute,如果其中没有你所需要的,你可以自己定义,但要避免重名。objectClass是一种特殊的Attribute,它包含其它用到的Attribute以及它自身。常见的Attribute如:givenName、l、objectClass、dc、ou、cn、c、mail、telephoneNumber、sn、uid等。分别介绍如下:

c:国家;
cn:common name,指一个对象的名字;如果指人,需要使用其全名;
dc:domain Component,经常用来指一个域名的一部分,如:dc=mydomain,dc=org;
givenName:指一个人的名字,不能用来指姓或者middle name;
l:指一个地名,如一个城市或者其它地理区域的名字;
mail:电子信箱地址
o:organizationName,指一个组织的名字;
objectClass:一个LDAP server要想启用必须能够识别每一个对象的Attribute,objectClass Attribute正是用来描述一个对象应该具有的Attribute及可选Attribute。因此,每个objectClass“模板”的Attribute中必然含有一条objectClass Attribute,我不知道用“自包含”称呼这个算不算合适。
ou:organizationalUnitName,指一个组织单元的名字。
sn:surname,指一个人的姓;
telephoneNumber:电话号码,应该带有所在的国家的代码;
uid:userid,通常指一个人的登录名,这个不同于Linux系统中用户的uid;

     如果可以这样类比的话,我想,我们不妨把objectClass理解为关系数据库的表,而attribute则类同为表中的字段。而下面即可介绍的schema或许可以类比作一个数据库,但它的这个类比或许从逻辑上说更合适些。

六、什么是schema

         好了,现在可以说说到底什么是schema了。LDAP中,schema用来指定一个目录中所包含的objects的类型(objectClass)以及每一个objectClass中的各个必备(mandatory)和可选(optional)的属性(attribute)。因此,Schema是一个数据模型,它被用来决定数据怎样被存储,被跟踪的数据的是什么类型,存储在不同的Entry下的数据之间的关系。schema需要在主配置文件slapd.conf中指定,以用来决定本目录中使用到的objectClass。管理员可以自己设计制定schema,一般包括属性定义(AttributeDefinition)、类定义(ClassDefinition)以及语法定义(SyntaxDefinition)等部分。  

     LDAP V3中在x.500标准的基础上定义了一个包含了网络中大多常见对象的schema,这些对象包括国家、所在地、组织、人员、小组以及设备等。同时,LDAP V3中可以很方便的从目录中提取出schema,它正是一条记录中关于属性的声明部分。

七、对象标识符(Object Identifiers)

      对象标识符(OID)是被LDAP内部数据库引用的数字标识。Attribute的名字是设计为方便人们读取的,但为了方便计算机的处理,通常使用一组数字来标识这些对象,这类同于SNMP中的MIB2。例如,当计算机接收到dc这个Attribute时,它会将这个名字转换为对应的OID:1.3.6.1.4.1.1466.115.121.1.26。


八、使用LDAP做身份验正

     验正主要是用来确定一次会主中客户端用户所具有的权利,即用来确立用户能否登录以及登录具有使用哪些资源以及如何使用资源的权限。验正过程中的修改、查询等操作由认证级别来控制。

     objectClass中的person可以用来作linux系统中用户登入的身份验正,此时需要指定userPassword属性的值,即指定用户登入时使用的密码。密码可以使用的加密方式有MD5、CRYPT、SHA、SSHA等。在LDAP V3中,验正客户端时可以使用的验正机制有匿名验正、简单验正、基于SSL/TLS的验正和基于SASL的验正等四种方式。

第二部分:安装配置Openldap-2.3.32

     目前有许多种软件可以实现LDAP,如Windows AD,Openldap等。我们要实现的是在RedHat9.0上安装Openldap来实现其应用。

     为了让openldap支持ssl/tls和sasl,我们这次的安装将首先安装openssl0.98e和cyrus-sasl-2.0.22这两个软件包。

一、安装openssl0.98e

1.1 下载相关软件包至/usr/local/src目录

http://www.openssl.org/source/openssl-0.9.8e.tar.gz

1.2 安装openssl

#cd /usr/local/src
#tar zxvf openssl-0.9.8e.tar.gz
#cd openssl-0.9.8e
#./config shared zlib
#make
#make test
#make install
mv /usr/bin/openssl /usr/bin/openssl.OFF
mv /usr/include/openssl /usr/include/openssl.OFF
ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/ssl/include/openssl /usr/include/openssl

1.3 配置库文件搜索路径

#echo "/usr/local/ssl/lib" >> /etc/ld.so.conf
#ldconfig -v

1.4 查看openssl的版本号,以验正是否安装正确

#openssl version -a
OpenSSL 0.9.8e 17 Apr 2007
built on: Sat Mar 24 21:24:41 CST 2007
platform: linux-elf
options:   bn(64,32) md2(int) rc4(idx,int) des(ptr,risc1,16,long) idea(int) blowfish(idx)
compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DTERMIO -O3 -fomit-frame-pointer -Wall -DOPENSSL_BN_ASM_PART_WORDS -DOPENSSL_IA32_SSE2 -DSHA1_ASM -DMD5_ASM -DRMD160_ASM -DAES_ASM
OPENSSLDIR: "/usr/local/ssl"


二、安装cyrus-sasl-2.0.22

2.1 下载cyrus-sasl-2.1.22到/usr/local/src
http://ftp.andrew.cmu.edu/pub/cyrus-mail/cyrus-sasl-2.1.22.tar.gz

2.2 编译安装

#tar zxvf cyrus-sasl-2.1.22.tar.gz
#cd   cyrus-sasl-2.1.22
# ./configure --prefix=/usr/local/sasl2 --enable-login --with-openssl=/usr/local/ssl
#make
#make install

2.3配置库文件搜索路径

#echo "/usr/local/sasl2/lib" >> /etc/ld.so.conf
#echo "/usr/local/sasl2/lib/sasl2" >> /etc/ld.so.conf
#ldconfig -v

2.4 把Redhat9.0原有的sasl库文件改名或删除,并将相关符号链接指向新安装SASL的库文件

#cd /usr/lib
# mv libsasl2.a libsasl2.a.OFF
# mv libsasl2.la libsasl2.la.OFF
# mv libsasl2.so libsasl2.so.OFF
# mv libsasl2.so.2.0.10 libsasl2.so.2.0.10.OFF
# mv lIbsasl2.so.2 libsasl2.so.2.OFF

# ln -s /usr/local/sasl2/lib/* /usr/lib
# ln -s /usr/local/sasl2/lib/sasl2 /usr/lib/sasl2

#ln -s /usr/local/sasl2/lib/libsasl2.so.2.0.22 /usr/lib/libsasl2.so.2
#ln -s /usr/local/sasl2/lib/libsasl2.so /usr/lib/libsasl2.so

2.5 创建运行时需要的目录并调试启动

#mkdir -pv /var/state/saslauthd      

# ./saslauthd -a pam shadow -d
saslauthd[3533] :main             : num_procs   : 5
saslauthd[3533] :main             : mech_option: NULL
saslauthd[3533] :main             : run_path    : /var/state/saslauthd
saslauthd[3533] :main             : auth_mech   : pam
saslauthd[3533] :ipc_init         : using accept lock file: /var/state/saslauthd/mux.accept
saslauthd[3533] :detach_tty       : master pid is: 0
saslauthd[3533] :ipc_init         : listening on socket: /var/state/saslauthd/mux
saslauthd[3533] :main             : using process model
saslauthd[3534] :get_accept_lock : acquired accept lock      (此处使用"ctrl+c"退出)
saslauthd[3533] :have_baby        : forked child: 3534
saslauthd[3533] :have_baby        : forked child: 3535
saslauthd[3533] :have_baby        : forked child: 3536
saslauthd[3533] :have_baby        : forked child: 3537

2.6 启动并测试验正

#/usr/local/sasl2/sbin/saslauthd -a pam shadow

#/usr/local/sasl2/sbin/testsaslauthd -u root -p root的密码
0: OK "Success."

三、安装BerkeleyDB

3.1 下载相关软件至/usr/local/src

http://www.oracle.com/technology ... .5.20/db-targz.html

3.2 编译安装

#tar zxvf db-4.5.20.tar.gz
#cd db-4.5.20/build_unix
#../dist/configure --prefix=/usr/local/BerkeleyDB
#make
#make install

四、安装openldap-2.3.32

4.1 下载相关软件至/usr/local/src

http://www.openldap.org/software ... stable-20070110.tgz

4.2 编译安装

#tar zxvf openldap-stable-20070110.tgz
#cd openldap-2.3.32
#env CPPFLAGS="-I/usr/local/BerkeleyDB/include -I/usr/local/sasl2/include" LDFLAGS="-L/usr/local/BerkeleyDB/lib -L/usr/local/sasl2/lib -L/usr/local/sasl2/lib/sasl2"   ./configure --prefix=/usr/local/openldap --sysconfdir=/etc/openldap --enable-passwd --enable-wrappers --disable-ipv6 --enable-spasswd --enable-crypt --enable-modules   --enable-accesslog=yes
#make depend
#make
#make test
#make install
#cp /usr/local/openldap/var/openldap-data/DB_CONFIG.example /usr/local/openldap/var/openldap-data/DB_CONFIG

4.3 测试启动

# /usr/local/openldap/libexec/slapd -d 256
@(#) $OpenLDAP: slapd 2.3.32 (Apr 17 2007 00:44:16) $
        root@localhost.localdomain:/usr/local/src/openldap-2.3.32/servers/slapd
Expect poor performance for suffix dc=my-domain,dc=com.
slapd starting

4.4 启动及关闭服务

启动
#/usr/local/openldap/libexec/slapd

查看监听的端口
# netstat -tunlp |grep :389
tcp         0       0 0.0.0.0:389              0.0.0.0:*                LISTEN       10111/slapd

查看能否正常查询
# /usr/local/openldap/bin/ldapsearch -x -b '' -s base '(objectclass=*)' (此行为命令,以下为结果)
# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: (objectclass=*)
# requesting: ALL
#

#
dn:
objectClass: top
objectClass: OpenLDAProotDSE

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1


关闭
#kill -INT `cat /usr/local/openldap/var/run/slapd.pid`


五、主配置文件slapd.conf介绍

安全起见,slapd.conf文件应该只让运行此进程的用户可读写。下面是安装完毕后的一个slapd.conf示例。

#
# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.
#
include          /etc/openldap/openldap/schema/core.schema

# Define global ACLs to disable default read access.

# Do not enable referrals until AFTER you have a working directory
# service AND an understanding of referrals.
#referral        ldap://root.openldap.org

pidfile          /usr/local/openldap/var/run/slapd.pid
argsfile         /usr/local/openldap/var/run/slapd.args

# Load dynamic backend modules:
# modulepath     /usr/local/openldap/libexec/openldap
# moduleload     back_bdb.la
# moduleload     back_ldap.la
# moduleload     back_ldbm.la
# moduleload     back_passwd.la
# moduleload     back_shell.la

# Sample security restrictions
#        Require integrity protection (prevent hijacking)
#        Require 112-bit (3DES or better) encryption for updates
#        Require 63-bit encryption for simple bind
# security ssf=1 update_ssf=112 simple_bind=64

# Sample access control policy:
#        Root DSE: allow anyone to read it
#        Subschema (sub)entry DSE: allow anyone to read it
#        Other DSEs:
#                Allow self write access
#                Allow authenticated users read access
#                Allow anonymous users to authenticate
#        Directives needed to implement policy:
# access to dn.base="" by * read
# access to dn.base="cn=Subschema" by * read
# access to *
#        by self write
#        by users read
#        by anonymous auth
#
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn.   (e.g., "access to * by * read")
#
# rootdn can always read and write EVERYTHING!

#############################################################
# BDB database definitions
#############################################################

database         bdb
suffix           "dc=my-domain,dc=org"
rootdn           "cn=Manager,dc=mydomain,dc=org"
# Cleartext passwords, especially for the rootdn, should
# be avoid.   See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw           secret
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 reorgmended.
directory        /usr/local/openldap/var/openldap-data
# Indices to maintain

index    objectClass      eq

接下来我们对上述部分进行一些介绍:

include          /etc/openldap/openldap/schema/core.schema

此句是用来将目录所用到的schema文件包含进来;openldap一般默认带有几个schema,在我们的配置文件安装目录下的schema目录中存放。本句中的core.schema是LDAP V3中必须的,它给出了LDAP V3中最基本的attribute和objects的定义。其它的还有:corba.schema、dyngroup.schema、java.schema   nis.schema、openldap.schema、cosine.schema、inetorgperson.schema、misc.schema、ppolicy.schema


pidfile          /usr/local/openldap/var/run/slapd.pid

此句用来定义slapd进程运行时的pid文件,需要使用绝对路径。

argsfile         /usr/local/openldap/var/run/slapd.args

此句用来定义包含当前正在运行的slapd进程所用到的命令行参数的文件,需要使用绝对路径。

# Load dynamic backend modules:
# modulepath     /usr/local/openldap/libexec/openldap
# moduleload     back_bdb.la
# moduleload     back_ldap.la
# moduleload     back_ldbm.la
# moduleload     back_passwd.la
# moduleload     back_shell.la

以上用来指定动态加载的后端模块。

# Sample security restrictions
#        Require integrity protection (prevent hijacking)
#        Require 112-bit (3DES or better) encryption for updates
#        Require 63-bit encryption for simple bind
# security ssf=1 update_ssf=112 simple_bind=64

以上是安全相关的声明语句。常用到的参数有五类,除了Require和security外还有Allow、Disallow和password-hash等。
Require参数用来使管理员指定访问目录时必须遵循的规则和条件;这种指定可以是全局的,也可以仅用来限制对某个后端数据库的访问限制。
security参数用来让管理员指定加强安全性的一般规则。

# Sample access control policy:
#        Root DSE: allow anyone to read it
#        Subschema (sub)entry DSE: allow anyone to read it
#        Other DSEs:
#                Allow self write access
#                Allow authenticated users read access
#                Allow anonymous users to authenticate
#        Directives needed to implement policy:
# access to dn.base="" by * read
# access to dn.base="cn=Subschema" by * read
# access to *
#        by self write
#        by users read
#        by anonymous auth
#
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn.   (e.g., "access to * by * read")
#
# rootdn can always read and write EVERYTHING!

以上主要是用来定义访问控制列表。

#######################################################################
# BDB database definitions
#######################################################################

database         bdb

定义使用的后端数据存储方式。其后可以跟的值有bdb、ldbm、passwd、config、dnssrv、ldif、perl、hdb和shell等。bdb指使用Berkley DB 4数据库。

suffix           "dc=my-domain,dc=org"

定义suffix,以上部分可以改作你的域名中相关的部分,如"dc=example,dc=com"。

rootdn           "cn=Manager,dc=mydomain,dc=org"

定义此目录的超级管理员帐号,类同于系统中的root。由于访问控制对此用户是不生效的,因此存在很大的安全隐患。建议安装配置及调试完成以后移除此帐号。

rootpw           secret

定义超级管理员帐号的密码,这里使用的是明文存储(secret即是其密码)的方式。这是极不安全的,建议使用加密方式存储,可以使用的加密方式有:CRYPT、MD5、SMD5、SHA和SSHA。产生加密密码散列的方法是使用slappasswd命令,用-h选项指明加密时使用的方式。示例如下:
# /usr/local/openldap/sbin/slappasswd -h {SSHA}
New password:
Re-enter new password:
{SSHA}k2H1GPM/95VfgsKg2jZv9hV+2GCH04hC


directory        /usr/local/openldap/var/openldap-data

这一句用来指定目录相关数据的存放位置。此目录最好只能由运行slapd进程的用户所有,推荐权限为700

index    objectClass      eq

此句用来指定slapd索引时用到的attribute,eq指索引时的匹配规则。主要是用来优化查询的。常用到的匹配规则有:approx(模糊匹配,approximate)、eq(精确匹配,equality)、pres(现值匹配,若某记录的此attribute没有值则不进行匹配,presence)和sub (子串匹配,substring)。

 

--------------

Ldap常用命令

1.ldapadd -x -D "cn=manager,ou=xxzx,o=netcenter,dc=ujs,dc=edu,dc=cn" -W -f first.ldif

 2.ldapsearch -x -b "ou=xxzx,o=netcenter,dc=ujs,dc=edu,dc=cn" '(objectclass=*)'

 ldapsearch -x -b "ou=xxzx,o=netcenter,dc=ujs,dc=edu,dc=cn" '(|(uid=*)(uid=*))' 或条件查询

 3.ldapsearch数据备份

 ldapsearch -h hostip -x -b "dc=js,dc=edu,dc=cn" -D "cn=admin,dc=ujs,dc=edu,dc=cn" -LLL "(objectclass=*)" -w password

LLL请看man ldapsearch,表示输出为ldif的第三版.

 4.ldapdelete -M -x -D "cn=manager,dc=edu,dc=cn" -W  "ou=xxzx,o=netcenter,dc=js,dc=edu,dc=cn"

 使用-M 可以删除referral,参见分布式openldap目录总结一文

 5.在使用referral时,双方的rootdn与suffix需要一致.

 6. ldapmodify -M -x -D "cn=ldap,dc=ujs,dc=edu,dc=cn" -W -f first.ldif

-------------

 

 

LDAP协议基础概念

1. 从用途上阐述LDAP,它是一个存储静态相关信息的服务,适合“一次记录多次读取”。常用LDAP服务存储的信息:

  • 公司的物理设备信息(如打印机,它的IP地址、存放位置、厂商、购买时间等)
  •  公开的员工信息(地址、电话、电子邮件…)
  •  合同和账号信息(客户信息、产品交付日期、投标信息、项目信息…)
  •  凭证信息(认证凭证、许可证凭证…)


2. 从数据结构上阐述LDAP,它是一个树型结构,能有效明确的描述一个组织结构特性的相关信息。在这个树型结构上的每个节点,我们称之为“条目(Entry)”,每个条目有自己的唯一可区别的名称(Distinguished Name ,DN)。条目的DN是由条目所在树型结构中的父节点位置(Base DN)和该条目的某个可用来区别身份的属性(称之为RDN如uid , cn)组合而成。对Full DN :“shineuserid=linly , ou=Employee , dc=jsoso , dc=net”而言,其中Base DN:“ou=Employee , dc=jsoso , dc=net”,RDN:“shineuserid=linly”下面是一个LDAP服务器的数据结构图:
 
3. 从协议衍化上阐述LDAP,它是“目录访问协议DAP——ISO X.500”的衍生,简化了DAP协议,提供了轻量级的基于TCP/IP协议的网络访问,降低了管理维护成本,但保持了强壮且易于扩充的信息框架。LDAP的应用程序可以很轻松的新增、修改、查询和删除目录内容信息。

LDAP目录条目(Directory Entry)简述
从Object Classes谈起
在LDAP目录数据库中,所有的条目都必须定义objectClass这个属性。这有点像Java语言里说阐述的“一切皆对象”的理念,每个条目(LDAP Entry)都要定义自己的Object Classes。Object Class可以看作是LDAP Entry的模板,它定义了条目的属性集,包括必有属性(requited attribute)和可选属性(option attribute)。这里要着重指出的是,在LDAP的Entry中是不能像关系数据库的表那样随意添加属性字段的,一个Entry的属性是由它所继承的所有Object Classes的属性集合决定的,此外可以包括LDAP中规定的“操作属性”(操作属性是一种独立于Object Class而存在的属性,它可以赋给目录中的任意条目)。如果你想添加的属性不在Object Classes定义属性的范畴,也不是LDAP规定的操作属性,那么是不能直接绑定(在LDAP中,给Entry赋予属性的过程称为绑定)到条目上的,你必须自定义一个含有你需要的属性的Object Class,而后将此类型赋给条目。
Object Class是可以被继承的,这使它看上去真的很像Java语言中的POJO对象。继承类的对象实例也必须实现父
类规定的必有属性(requited attribute),同时拥有父类规定的可选属性(option attribute)。继承类可以扩展父类的必有属性和可选属性。由于Object Class的继承特性,因此在一个LDAP Entry上,objectClass属性是一个多值属性,它涵盖了Object Class的完整继承树,如用户条目uid=Linly , ou=People, dc=jsoso , dc=net,它直接实现了inetorgperson这个对象类,那么它的objectClass属性值为inetorgperson,organizationalPerson,person,top。

从Object Classes到Directory Server Schema
上一章节中,我们了解了LDAP条目都要遵守的一个最重要的规定Object Classes,而实际上,对Entry更多更细的规范被涵盖在了Directory Server Schema(目录服务模式)中。Directory Schema声明了完整的LDAP数据的存储规范,这包括数据的字节大小、数值范围和格式定义。
默认的,在一个LDAP服务器上,都定义有一套标准的Schema和一套为服务器功能定制的Schema。用户在需要的时候,是可以定制自己的LDAP属性和Object Class,以扩展标准Schema的功能。在Sun Directory Server中,使用了标准LDAPv3 Schema,并在此基础上做了轻微的扩展。

在Schema中的标准属性(Standard Attributes)是一个键-值对,如:cn:linly ,属性ID(属性名)为cn,属性值为linly 。事实上,一个完整的条目就是由一系列的键-值对组成的。以下是一个完整的LDAP Entry:

引用
dn: uid=Linly,ou=People, dc=jsoso,dc=net
telephoneNumber: 13950491407
mail: linliangyi2005@gmail.com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetorgperson
cn: LinLiangyi
userPassword: {SSHA}aPTgP47LeziVGqjPBI8343FwkcL3QgQQ9kirXw==
creatorsName: uid=admin,ou=administrators,ou=topologymanagement,o=netscaperoot
createTimestamp: 20080219070003Z
nsUniqueId: 2deb0d01-deb811dc-8055dc88-5f880db9
nsRoleDN: cn=MyAdminRole,ou=People,dc=jsoso,dc=net
nsRoleDN: cn=secondRole,ou=People,dc=jsoso,dc=net
cn;phonetic;lang-zh:: IA==
preferredLanguage: zh-CN
cn;lang-zh:: 5p6X6Imv55uKICA=
givenName: liangyi
givenName;lang-zh:: 6Imv55uK
sn: lin
sn;lang-zh:: 5p6X
uid: linly
manager: cosTemplateForPostalCode
modifiersName: uid=admin,ou=administrators,ou=topologymanagement,o=netscaperoot
modifyTimestamp: 20080227083015Z



在Schema中,对属性的定义包含以下内容:

  •  一个唯一的属性名称
  •  一个属性的OID(object  identifier)
  •  一段属性的文本描述信息
  •  一个关联属性文法定义的OID
  •  属性的单值/多值描述;属性是否是目录自有的;属性的由来;附加的一些匹配规则



此外Schema中最重要的部分就是我们上面提到的Object Classes,它实际上是预定义的一套捆绑成套的属性集合。在Schema定义中,Object Classes要包含以下内容:

  •  一个唯一的名字
  •  一个object identifier (OID) 定义Object Class
  •  一个必有的属性集合
  •  一个可选的属性集合



高级LDAP条目
在目录服务中,信息是以条目的形式被分层次的组织在一起的。LDAP提供了几种分组机制,使得信息管理更富有弹性。

静态组和动态组(Static Group and Dynamic Group)
组(Group)
,声明一个目录条目的集合
静态组(Static Group):显式声明了一个它的集合成员,这种方式适用于少量明确的成员组合。
动态组(Dynamic Group):它定义了一个过滤条件,所有匹配条件的条目都是组的成员。所以称之为动态组,是因为每次读取其组员名单时,要动态计算过滤条件。
使用组的优点是能够快速的查找所属的成员;缺点是,给出任意的成员,无法获知它所属的组。因此从数据关联关系上看,Group适合一对多的查询。

受管角色、过滤器角色和嵌套角色(Managed Role、Filtered Role and Nested Role)
角色(Role)
,它是条目的另一种集合形式。它与组不同的在于,给定一个任意的成员条目,我们能立刻获知它所属的角色。因此从数据关联关系上看,Role适合多对一的查询。角色定义仅对它们的父节点子树下面的目录条目有效。
受管角色(Managed Role),它等价于Group中的静态组,不同的是,Role不是把组员信息添加到自身属性中,而是将自身的DN添加到组员条目的nsroledn属性中。
过滤器角色(Filtered Role),它与动态组相似,通过定义条目过滤器来确定组员。
嵌套角色(Nested Role),它是对角色定义的一种嵌套形式。可以嵌套其他的嵌套角色的。嵌套角色的成员,是其包含的所有角色成员的合集。嵌套角色通过包含从属于其它子树下的角色,可以扩展其搜索的scope。

服务类CoS
服务类实际上是一种属性的共享机制,它无须定义条目间的关联关系,却可以做到数据同步和空间优化。例如,在一个公司目录下,拥有上千个员工,他们拥有相同的公司地址属性;在传统的条目中,地址属性分别存贮在员工条目里,这样不但浪费存储空间,一旦地址变更,则要对员工条目进行逐一修改。采用CoS机制后,公司地址属性被存放在一个对象内,员工条目通过引用这个对象来获得地址信息,从而缩小的存储空间损耗,并方便了信息的修改。
CoS仅对其父节点子树下面的目录条目有效。CoS机制包含两个部分,CoS 定义条目和CoS模板条目。定义条目描述了属性是如何被引用的;模板条目描述了属性的值。CoS机制包含3种类型:
指针服务类(Pointer CoS),
在Pointer CoS中,CoS包含一个定义Definition Entry,它指定了两个属性:1.共享属性的名称;2.提供共享数据的模板DN。 另外CoS还要有一个Template Entry,它要提供共享的数据。
在定义了Definition Entry和Template Entry后,Pointer CoS将为其父节点子树下面的所有条目(目标条目Target Entry)分配共享属性和模板所定义的值。示意图如下:
 
 Definition Entry:cn=PointerCoS , dc=example , dc= com定义了CoS的共享属性cosAttribute:postalCode,同时定义了CoS的模板DN cosTemplateDN:cn=cosTemplateForPostalCode,cn=data。
 Template Entry: 它是CoS的模板,定义了共享属性值 postalCode:45773。
 Target Entry:它是目标条目,因为它位于dc=example , dc=com的子树下。所以它获得了共享属性postalCode:45773。

间接服务类(Indirect CoS),
在使用间接服务类时,在Definition Entry条目中定义了CoS的共享属性cosAttribut和一个用来间接指向模板的属性cosIndirectSpecifier。
首先,我们需要用cosIndirectSpecifier的值A作为属性名,来检索CoS父节点子树中所有拥有A属性的条目,作为目标条目Target Entry。
其次,根据找到的Target Entry条目中A属性的值来定位模板对象。
最后,再分别根据找到的模板对象中拥有的共享属性值赋给对应的Target Entry。
例如,定义如下Definition Entry

引用
dn: cn=generateDeptNum,ou=People,dc=example,dc=com
objectclass: top
objectclass: LDAPsubentry
objectclass: cosSuperDefinition
objectclass: cosIndirectDefinition
cosIndirectSpecifier: manager
cosAttribute: departmentNumber


该CoS定义对条目ou=People,dc=example,dc=com下的子树中所有具有manager属性的条目有效,同时设定其CoS模板指向manager属性的值所指向的条目。
又假定有如下的Template Entry条目,它具有属性departmentNumber:

引用
dn: cn=Carla Fuentes,ou=People,dc=example,dc=com

objectclass: cosTemplate

departmentNumber: 318842


同时在ou=People,dc=example,dc=com下有Target Entry如下:

引用
dn: cn=Babs Jensen,ou=People,dc=example,dc=com
cn: Babs Jensen
...
manager: cn=Carla Fuentes,ou=People,dc=example,dc=com
departmentNumber: 318842


因为该Entry具有manager属性,且在ou=People,dc=example,dc=com子树下,所以它成为了Target Entity。并且由于其manager的值指向模板cn=Carla Fuentes,ou=People,dc=example,dc=com,因此,它的departmentNumber为 318842。

经典服务类(Classic CoS)
经典服务类同间接服务类有点相似,它也是对属性的间接应用。在Classic CoS的定义条目中,除了共享属性定义外,还有两个定义,一个是cosTemplateDn,它指向模板条目的父节点;另一个是cosSpecifier,它的值指向目标条目的属性A。由目标条目的属性A的值来代替模板条目的RND。则目标条目的属性A的值加上cosTemplateDn的值恰好定义一个唯一的模板条目。
举例如下,首先是一个经典服务类的定义条目:

引用
dn: cn=classicCoS,dc=example,dc=com
objectclass: top
objectclass: LDAPsubentry
objectclass: cosSuperDefinition
objectclass: cosClassicDefinition
cosTemplateDn: ou=People,dc=example,dc=com
cosSpecifier: building
cosAttribute: postalAddress


该定义条目指明了3个参数,
1. 要共享的属性是postalAddress
2. 模板条目的上下文前缀是ou=People,dc=example,dc=com
3. 模板条目的RDN存储于目标条目的building属性中
其次,假定有如下模板条目:

引用
dn: cn=B07,ou=People,dc=example,dc=com
objectclass: top
objectclass: LDAPsubentry
objectclass: extensibleobject
objectclass: cosTemplate
postalAddres: 7 Old Oak Street$Anytown, CA 95054


最后,我们假设有以下目标条目:

引用
dn: cn=Babs Jensen,ou=People,dc=example,dc=com
cn: Babs Jensen
...
building: B07
postalAddres: 7 Old Oak Street$Anytown, CA 95054


由于目标条目中,building属性的值是B07,因此该条目的模板定义DN = B07加上ou=People,dc=example,dc=com ,即cn=B07,ou=People,dc=example,dc=com,因此目标条目的postalAddres 引用模板的值7 Old Oak Street$Anytown, CA 95054。

LDAP 目录搜索
LDAP搜索是目录服务最常用的功能之一。在LDAP服务中搜索要用到相应的Filter语句。Filter语句由3个部分组成:
1. 属性,如:cn ,uid ,操作属性如:objectClass  ,  nsroledn
2. 比较操作符 ,如 < , > ,= ,…
3. 逻辑预算符,如: 与操作&  , 或操作| , 非操作!
关于Filter语句组成的详细参数表如下:
filter的运算符
 
filter布尔运算符
 

搜索过滤器示例

  • 下列过滤器将搜索包含一个或多个 manager 属性值的条目。这也称为存在搜索:manager=*
  • 下列过滤器将搜索包含通用名 Ray Kultgen 的条目。这也称为等价搜索:cn=Ray Kultgen
  • 下列过滤器返回所有不包含通用名 Ray Kultgen 的条目:(!(cn=Ray Kultgen))
  • 下列过滤器返回的所有条目中都有包含子字符串 X.500 的说明属性:description=*X.500*
  • 下列过滤器返回所有组织单元为 Marketing 且说明字段中不包含子字符串 X.500 的条目:(&(ou=Marketing)(!(description=*X.500*)))
  • 下列过滤器返回所有组织单元为 Marketing 且 manager 为 Julie Fulmer 或 Cindy Zwaska 的条目:(&(ou=Marketing)(|(manager=cn=Julie Fulmer,ou=Marketing,dc=siroe,dc=com)(manager=cn=Cindy Zwaska,ou=Marketing,dc=siroe,dc=com)))
  • 下列过滤器返回所有不代表人员的条目:(!(objectClass=person))
  • 下列过滤器返回所有不代表人员且通用名近似于 printer3b 的条目:(&(!(objectClass=person))(cn~=printer3b))



ldapsearch指令参数-b 搜索的起始上下文

  • -D 绑定搜索的账号Distinguished Name
  • -h 主机名。地址
  • -p LDAP服务端口
  • -l 搜索的最大耗时
  • -s 从上下文开始的搜索范围,有三个常量base(表示仅当前根对象)/one(当前根对象及下一级)/sub(当前根对象的全部子树)
  • -W 绑定账号密码
  • -z 返回结果的最大数量



搜索“操作属性”
在LDAP搜索中,操作属性在默认情况下是不会跟随搜索结果返回的。必须在搜索中明确显示的指定操作属性,如:

引用
ldapsearch -h linly.jsoso.net -p 5201 -D "cn=directory manager" -w password "objectclass=*" aci=accounts。



搜索“操作对象类”的条目
在LDAP中Role、CoS等对象被定义为特殊的Object Class——操作对象类(operational object class),在一般的搜索中,这类对象是不会作为结果返回给用户的。要想查找这些对象,必须在filter中显式定义要找这个对象类。例如:(objectclass=ldapsubentry)。

ACI权限控制
ACI(Access Control Instruction)访问控制指令是LDAP 服务中用以控制用户访问权限的有力手段。在目录的Entry中,aci属性记录了对该条目的多条访问控制指令。(aci属性是一个多值操作属性,可以赋予任意的LDAP条目)
ACI的语法格式如下:aci: (target)(version 3.0;acl "name";permission bind_rules;)
 target 指定了ACI要控制访问的目标属性(集合)或条目(集合)。target可以用DN,一个或多个属性,或者一个filter来定义。它是一个可选项。
target语法是:关键字 <op> 表达式
target关键字表
 
(更多详细的target用法,请参阅 Sun ONE Directory Server 5.2 Administration Guide )

version 3.0 这是一个必须的常量字窜,用以识别ACI的版本。

name 指定ACI的名称,可以使任意的字窜,只要区别于同一个条目aci属性下的其他ACI,这是一个必须属性。

permission 指定权限许可。
权限包括:read、write、add、delete、search、compare、selfwrite、 proxy 或 all,其中all表示出了proxy之外的所有操作。
权限语法:allow | deny (权限)

bind_rules 绑定规则。绑定规则定义了何人、何时,以及从何处可以访问目录。绑定规则可以是如下规则之一:

  • • 被授予访问权限的用户、组以及角色
  • • 实体必须从中绑定的位置
  • • 绑定必须发生的时间或日期
  • • 绑定期间必须使用的验证类型


绑定规则语法:keyword  = 或者 != "expression_r"; (注:timeofday 关键字也支持不等式<、<=、>、>=)。
LDIF 绑定规则关键字表
 

(更多详细的绑定规则用法,请参阅 Sun ONE Directory Server 5.2 Administration Guide )

ACI样例

  • 1.用户 bjensen 具有修改其自己的目录条目中所有属性的权限。 aci:(target="ldap:///uid=bjensen,dc=example,dc=com")(targetattr=*)(version 3.0; acl "aci1"; allow (write) userdn="ldap:///self";)
  • 2.允许 Engineering Admins 组的成员修改 Engineering 业务类别中所有条目的 departmentNumber 和 manager 属性 aci:(targetattr="departmentNumber || manager")(targetfilter="(businessCategory=Engineering)") (version 3.0; acl "eng-admins-write"; allow (write) groupdn ="ldap:///cn=Engineering Admins, dc=example,dc=com";)
  • 3.允许匿名用户对o=NetscapeRoot下的条目读取和搜索 aci:(targetattr="*")(targetfilter=(o=NetscapeRoot))(version 3.0; acl "Default anonymous access"; allow (read, search) userdn="ldap:///anyone";)
  • 4.向所有经过验证的用户授予对整个树的读取访问,可以在dc=example,dc=com 节点创建下列 ACI: aci:(version 3.0; acl "all-read"; allow (read)userdn="ldap:///all";)
  • 5.允许对整个 example.com 树进行匿名读取和搜索访问,可以在dc=example,dc=com 节点创建下列 ACI: aci:(version 3.0; acl "anonymous-read-search";allow (read, search) userdn = "ldap:///anyone";)
  • 授予Administrators 组对整个目录树写入的权限,则可以在 dc=example,dc=com 节点创建下列 ACI: aci:(version 3.0; acl "Administrators-write"; allow (write) groupdn=ldap:///cn=Administrators,dc=example,dc=com;)
    Java6.0 API for LDAP概述

    从JDK5.0开始,对LDAP协议的数据访问操作就被集成在javax的扩展API包中,并随同JDK一并发布,这一章节,我们主要介绍API包中的类信息。
    javax.naming.directory 包的结构
     

    常用API解析

    javax.naming.directory.InitialDirContext,初始化目录服务上下文类
    该类是LDAP数据内容的操作工具类,通过该类可以执行绑定LDAP服务器、新增LDAP条目、获取条目实例、修改条目属性、删除条目和根据条件搜索条目等操作。常用方法说明如下:

    初始化LDAP 目录服务上下文(相当于使用JDBC打开一个数据库链接)
    • InitialDirContext(Hashtable<?,?> environment)

    绑定/创建LDAP条目对象(相当于新增一个LDAP条目数据bind(Name
    • name, Object obj, Attributes attrs)
    • bind(String name, Object obj, Attributes attrs)
    • createSubcontext(Name name, Attributes attrs)
    • createSubcontext(String name, Attributes attrs)

    获取条目实例(属性集)
    • getAttributes(Name name)
    • getAttributes(Name name, String[] attrIds)
    • getAttributes(String name)
    • getAttributes(String name, String[] attrIds)

    修改条目属性
    • modifyAttributes(Name name, int mod_op, Attributes attrs)
    • modifyAttributes(Name name, ModificationItem[] mods)
    • modifyAttributes(String name, int mod_op, Attributes attrs)
    • modifyAttributes(String name, ModificationItem[] mods)

    删除条目
    • destroySubcontext(Name name)
    • destroySubcontext(String name)

    根据属性集搜索条目
    • search(Name name, Attributes matchingAttributes)
    • search(Name name, Attributes matchingAttributes, String[] attributesToReturn)
    • search(String name, Attributes matchingAttributes)
    • search(String name, Attributes matchingAttributes, String[] attributesToReturn)

    根据过滤器搜索条目
    • search(Name name, String filterExpr, Object[] filterArgs, SearchControls cons)
    • search(Name name, String filter, SearchControls cons)
    • search(String name, String filterExpr, Object[] filterArgs, SearchControls cons)
    • search(String name, String filter, SearchControls cons)


    javax.naming.directory.BasicAttribute,LDAP基本属性对象
    该类用来表示LDAP条目中的单个属性对象。在目录服务中,每个属性名称是可以对应多个的属性值的。

    构建属性对象
    • BasicAttribute(String id)
    • BasicAttribute(String id, boolean ordered)
    • BasicAttribute(String id, Object value)
    • BasicAttribute(String id, Object value, boolean ordered)

    添加属性值
    • add(int ix, Object attrVal),添加属性值到多值属性的指定位置
    • add(Object attrVal) , 追加属性值到多值属性尾部

    判断属性值是否包含
    • contains(Object attrVal) , 多值属性中有一个值是匹配的,返回true

    获取属性值
    • get(),取得属性值中的一个
    • get(int ix),从多值属性中的指定位置取值

    获取属性ID
    • getID(),属性的ID就是属性名

    删除属性值
    • remove(int ix),删除指定位置的属性值
    • remove(Object attrval),删除指定的属性值


    javax.naming.directory.BasicAttributes,LDAP实体的属性集
    该类表示一个LDAP条目绑定的属性集合,在绝大多数情况下,一个LDAP条目存在多个属性。

    构造属性集
    • BasicAttributes()
    • BasicAttributes(boolean ignoreCase),属性ID是否大小写敏感,建议不要使用敏感
    • BasicAttributes(String attrID, Object val)
    • BasicAttributes(String attrID, Object val, boolean ignoreCase)

    获取属性集中的单个属性对象
    • get(String attrID)

    获取全部属性的枚举
    • getAll()

    获取全部属性的ID枚举
    • getIDs()

    添加新属性
    • put(Attribute attr)
    • put(String attrID, Object val)

    移除指定属性
    • remove(String attrID)


    javax.naming.directory.SearchControls , LDAP目录服务搜索控制对象
    该类负责控制LDAP搜索行为的范围、设定返回结果数上限,搜索耗时上限,指定结果所包括的属性集等。

    设定搜索行为的范围
    • setSearchScope(int scope)

    设定返回结果数上限
    • setCountLimit(long limit)

    设定搜索耗时上限
    • setTimeLimit(int ms) , 以毫秒为单位

    指定结果所包括的属性集
    • setReturningAttributes(String[] attrs)


    javax.naming.directory.SearchResult , 表示.search() 方法的返回结果集中的一项。
    SearchResult类是对LDAP条目属性集的封装。在search()操作中可能返回完整的条目属性,也可能是条目属性的一部分。

    获取SearchResult封装的条目属性
    • getAttributes()


    以上只列举了LDAP操作API的常用部分,更多更详细的描述,请参考 Sun Java6.0 API DOC。LDAP操作代码样例
    在这个章节中,我们将结合LDAP操作的代码实例了解API使用。

    初始化LDAP 目录服务上下文
    该例子中,我们使用uid=linly,ou=People,dc=jsoso,dc=net这个账号,链接位于本机8389端口的LDAP服务器(ldap://localhost:8389),认证方式采用simple类型,即用户名/密码方式。
    1. private static void initialContext() throws NamingException{   
    2.     if(singleton == null){   
    3.         singleton = new LDAPConnection();   
    4.           
    5.         //LDAP服务地址   
    6.         singleton.sLDAP_URL = "ldap://localhost:8389";    
    7.         //管理员账号   
    8.         singleton.sMANAGER_DN = "uid=linly,ou=People,dc=jsoso,dc=net";   
    9.         //管理员密码   
    10.         singleton.sMANAGER_PASSWORD = "coffee";   
    11.         //认证类型   
    12.         singleton.sAUTH_TYPE = "simple";   
    13.         //JNDI Context工厂类   
    14.         singleton.sCONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";    
    15.            
    16.         singleton.envProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, singleton.sCONTEXT_FACTORY);   
    17.         singleton.envProps.setProperty(Context.PROVIDER_URL, singleton.sLDAP_URL);   
    18.         singleton.envProps.setProperty(Context.SECURITY_AUTHENTICATION, singleton.sAUTH_TYPE);   
    19.         singleton.envProps.setProperty(Context.SECURITY_PRINCIPAL, singleton.sMANAGER_DN);   
    20.         singleton.envProps.setProperty(Context.SECURITY_CREDENTIALS, singleton.sMANAGER_PASSWORD);   
    21.           
    22.         singleton.dirCtx = new InitialDirContext(singleton.envProps);   
    23.     }   
    24. }  
    通过一个Hashtable或者Properties对象为LDAP的Context设置参数,而后初始化InitialDirContext,即可绑定LDAP服务。这相当于JDBC中获取数据库的Connection对象。 
    
    绑定/创建LDAP条目对象 
    用户可以使用bind方法创建新的LDAP条目,下面的代码创建一个DN:"ou=Employee , dc=jsoso ,dc=net"的OrganizationUnit类LDAP条目如下: 
    
    1. public boolean createOrganizationUnit(){   
    2.     String ldapGroupDN = "ou=Employee , dc=jsoso ,dc=net";   
    3.     try {   
    4.           
    5.         Attributes attrs = dirContext.getAttributes(ldapGroupDN);   
    6.         System.out.println("Find the group , attributes list :");   
    7.         NamingEnumeration<String>  nEnum = attrs.getIDs();               
    8.         for( ; nEnum.hasMore() ; ){   
    9.             String attrID = nEnum.next();   
    10.             Attribute attr = (Attribute)attrs.get(attrID);   
    11.             System.out.println(attr.toString());   
    12.         }              
    13.         return false;   
    14.     } catch (NamingException e) {   
    15.           
    16.         //创建objectclass属性   
    17.         Attribute objclass = new BasicAttribute("objectclass");   
    18.         objclass.add("top");   
    19.         objclass.add("organizationalunit");    
    20.         //创建cn属性   
    21.         Attribute cn = new BasicAttribute("ou""Employee");    
    22.         //创建Attributes,并添加objectclass和cn属性   
    23.         Attributes attrs = new BasicAttributes();    
    24.         attrs.put(objclass);    
    25.         attrs.put(cn);    
    26.         //将属性绑定到新的条目上,创建该条目   
    27.         try {   
    28.             dirContext.bind(ldapGroupDN, null, attrs);   
    29.             System.out.println("Group created successful");   
    30.             return true;   
    31.         } catch (NamingException e1) {   
    32.             e1.printStackTrace();   
    33.         }              
    34.     }   
    35.     return false;   
    36. }  
    或者使用createSubcontext方法创建亦可,以下例子我们新增一个inetorgperson类的LDAP条目: 
    
    Java代码   复制代码
    1. public boolean createOrganizationUnit(){   
    2.     String ldapGroupDN = "ou=Employee , dc=jsoso ,dc=net";   
    3.     try {   
    4.           
    5.         Attributes attrs = dirContext.getAttributes(ldapGroupDN);   
    6.         System.out.println("Find the group , attributes list :");   
    7.         NamingEnumeration<String>  nEnum = attrs.getIDs();               
    8.         for( ; nEnum.hasMore() ; ){   
    9.             String attrID = nEnum.next();   
    10.             Attribute attr = (Attribute)attrs.get(attrID);   
    11.             System.out.println(attr.toString());   
    12.         }              
    13.         return false;   
    14.     } catch (NamingException e) {   
    15.           
    16.         //创建objectclass属性   
    17.         Attribute objclass = new BasicAttribute("objectclass");   
    18.         objclass.add("top");   
    19.         objclass.add("organizationalunit");    
    20.         //创建cn属性   
    21.         Attribute cn = new BasicAttribute("ou""Employee");    
    22.         //创建Attributes,并添加objectclass和cn属性   
    23.         Attributes attrs = new BasicAttributes();    
    24.         attrs.put(objclass);    
    25.         attrs.put(cn);    
    26.         //将属性绑定到新的条目上,创建该条目   
    27.         try {   
    28.             dirContext.bind(ldapGroupDN, null, attrs);   
    29.             System.out.println("Group created successful");   
    30.             return true;   
    31.         } catch (NamingException e1) {   
    32.             e1.printStackTrace();   
    33.         }              
    34.     }   
    35.     return false;   
    36. }
      获取条目属性
      下面一段代码获取entryDN参数指定条目中的属性集合,并打印到控制台
    Java代码   复制代码
    1.   
    2. public void find(String entryDN){   
    3.     try {    
    4.         Attributes attrs = dirContext.getAttributes(entryDN);    
    5.         if (attrs != null) {   
    6.             NamingEnumeration<String>  nEnum = attrs.getIDs();   
    7.             for( ; nEnum.hasMore() ; ){   
    8.                 String attrID = nEnum.next();   
    9.                 Attribute attr = (Attribute)attrs.get(attrID);   
    10.                 System.out.println(attr.toString());   
    11.             }   
    12.             System.out.println();   
    13.         }else{    
    14.             System.out.println("No found binding.");   
    15.         }    
    16.     }catch(NamingException ne) {    
    17.         ne.printStackTrace();   
    18.     }    
    19. }  

     

    修改条目属性
    修改DN=user.getDistinguishedName()的条目中的cn、givenname、sn和userpassword四个属性值。
    (注:参数DirContext.REPLACE_ATTRIBUTE有另外两个常量:DirContext.ADD_ATTRIBUTE;DirContext.REMOVE_ATTRIBUTE,分别表示新增属性和删除属性。)

    Java代码   复制代码
    1.     
    2. public boolean modifyUser(LDAPUser user) throws Exception {    
    3.     //用户对象为空    
    4.     if (user == null) {    
    5.         throw new Exception("No user information!n");    
    6.     }   
    7.   
    8.     //检查uid    
    9.     String userDN = user.getDistinguishedName();    
    10.     if (userDN == null && userDN.length() == 0) {    
    11.         throw new NamingException("No userDN you specify!n");    
    12.     }    
    13.   
    14.     //判断用户条目是否已经存在   
    15.     if(!isUserexist(userDN)){   
    16.         return false;   
    17.     }   
    18.        
    19.     //设置属性    
    20.     Attributes attrs = new BasicAttributes();    
    21.     setBasicAttribute(attrs, "cn", user.getCommomName());    
    22.     setBasicAttribute(attrs, "givenname", user.getFirstName());    
    23.     setBasicAttribute(attrs, "sn", user.getLastName());    
    24.     setBasicAttribute(attrs, "userpassword", user.getPassword());      
    25.     //修改属性    
    26.     try{   
    27.         dirContext.modifyAttributes(user.getDistinguishedName(),DirContext.REPLACE_ATTRIBUTE, attrs);    
    28.         System.out.println("User(" + user.getDistinguishedName() + ") information modified.n");   
    29.         return true;   
    30.     }catch(NamingException ne){   
    31.         ne.printStackTrace();   
    32.     }   
    33.     return false;   
    34. 删除条目
      删除DN= userDN条目

      Java代码   复制代码
      1.   
      2. public boolean deleteUser(String userDN) {    
      3.     if(!isUserexist(userDN)) {    
      4.         return true;   
      5.     }    
      6.     try {   
      7.         dirContext.destroySubcontext(userDN);   
      8.         System.out.println("User( " + userDN + ") deleted.n");    
      9.         return true;   
      10.     } catch (NamingException e) {   
      11.         e.printStackTrace();   
      12.     }    
      13.     return false;          
      14. }  



      根据属性集搜索条目
      根据属性集matchingAttributes中的匹配值,在上下文DN= "ou=People,dc=jsoso ,dc=net"中搜索它的所有子树中的匹配条目。
      (注:SearchControls的SCOPE参数详见SearchControls SCOPE补充说明)

       
          
      Java代码   复制代码
      1.            
      2. public void searchByAttribute(Attributes matchingAttributes){   
      3.      String baseDN = "ou=People,dc=jsoso ,dc=net";   
      4.      SearchControls cons = new SearchControls();   
      5.      cons.setSearchScope(SearchControls.SUBTREE_SCOPE);   
      6.      try {   
      7.         Name baseName = new LdapName(baseDN);   
      8.         NamingEnumeration<SearchResult> ne = dirContext.search(baseName, matchingAttributes);   
      9.         SearchResult entry = null;   
      10.         for(;ne.hasMore();){   
      11.             entry = ne.next();   
      12.             showEntry(entry);   
      13.         }                      
      14.     } catch (NamingException e) {   
      15.         e.printStackTrace();   
      16.     }   
      17. }  



      根据过滤器搜索条目
      根据过滤器条件,在上下文DN = "ou=People,dc=jsoso ,dc=net"中,搜索它的所有子树中的匹配条目。
      (注:过滤器filter的相关语法详见LDAP filter语法补充说明)

      Java代码   复制代码
      1.   
      2. public void searchByFilter(String filter){   
      3.      String baseDN = "ou=People,dc=jsoso ,dc=net";          
      4.      SearchControls cons = new SearchControls();   
      5.      cons.setSearchScope(SearchControls.SUBTREE_SCOPE);   
      6.      try {   
      7.         NamingEnumeration<SearchResult> ne = dirContext.search(baseDN, filter , cons);   
      8.         SearchResult entry = null;   
      9.         for(;ne.hasMore();){   
      10.             entry = ne.next();   
      11.             showEntry(entry);   
      12.         }                      
      13.     } catch (NamingException e) {   
      14.         e.printStackTrace();   
      15.     }   
      16. }  
        相关补充

        javax.naming.Name对象说明
        在API中,我们常常见到对LDAP上下文条目有两种参数形式,一种是大家都熟悉的String参数,如: String baseDN = "ou=People,dc=jsoso ,dc=net";
        另一种是使用javax.naming.Name类型的参数,那么Name类和String有什么区别呢?
        简单的说,Name是对String类DN的封装,它把一个完整的DN字窜分解成了RDN的list。这个list的顺序刚好和String相反。就拿"ou=People,dc=jsoso ,dc=net"为例,Name中的RDN顺序是[0]=net,[1]=jsoso,[2]=People。这样做的好处是更方便对Name进行操作,比如取其前缀或者后缀。

        SearchControls SCOPE补充说明
         

        LDAP filter语法补充说明
        filter的运算符

        filter布尔运算符


        搜索过滤器示例
      • 下列过滤器将搜索包含一个或多个 manager 属性值的条目。这也称为存在搜索: manager=*
      • 下列过滤器将搜索包含通用名 Ray Kultgen 的条目。这也称为等价搜索:cn=Ray Kultgen
      • 下列过滤器返回所有不包含通用名 Ray Kultgen 的条目:(!(cn=Ray Kultgen))
      • 下列过滤器返回的所有条目中都有包含子字符串 X.500 的说明属性:description=*X.500*
      • 下列过滤器返回所有组织单元为 Marketing 且说明字段中不包含子字符串 X.500 的条目: (&(ou=Marketing)(!(description=*X.500*)))
      • 下列过滤器返回所有组织单元为 Marketing 且 manager 为 Julie Fulmer 或 Cindy Zwaska 的条目: (&(ou=Marketing)(|(manager=cn=Julie Fulmer,ou=Marketing,dc=siroe,dc=com)(manager=cn=Cindy Zwaska,ou=Marketing,dc=siroe,dc=com)))
      • 下列过滤器返回所有不代表人员的条目: (!(objectClass=person))
      • 下列过滤器返回所有不代表人员且通用名近似于 printer3b 的条目:(&(!(objectClass=person))(cn~=printer3b))

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值