zoukankan      html  css  js  c++  java
  • openldap+php-ldap操作

    一、基础知识
    首先,如果您对LDAP 不认识,建议先看看[原]LDAP服务介绍一文。本文以Linux 下常用的OpenLDAP为例说明。
    LDAP 以数方式存放数据,每个节点可存放属性或作为下面节点的父节点。DN是作为某个节点的唯一标识,例如:

    ou=mail.linuxfly.org,dc=linuxfly,dc=org


    操作时,必须指定DN的位置。与普通数据库类似,操作LDAP,主要就是添加、删除或修改节点、其属性值等。
    属性必须要遵循.schema模式文件中定义的规则,这些文件通常放在/etc/openldap/schema/目录下,在/etc/openldap/sldapd.conf 中载入后生效,并且对载入顺序有一定的要求。

    引用
    # cat /etc/openldap/slapd.conf |grep include
    include         /etc/openldap/schema/core.schema
    include         /etc/openldap/schema/cosine.schema
    include         /etc/openldap/schema/inetorgperson.schema
    include         /etc/openldap/schema/nis.schema
    include         /etc/openldap/schema/rfmail.schema


    LDAP没有冗余性或唯一性要求,因此,在程序应用时,通常需手动创建属性,并赋值(指向)其他节点。
    通常情况下,ldap 都是全局可读的。也就是说,默认配置下,不会限制读取ldap 中的信息。其运行端口为:389 。

    二、登陆信息
    当需要修改或添加ldap 数据时,需要提供登陆信息,这些信息在/etc/openldap/sldap.conf 中提供:

    引用
    database        bdb #后台数据库类型
    suffix "dc=rfmail"  #目录树后缀
    rootdn "cn=root,dc=rfmail"  #管理员DN信息
    rootpw linuxfly.com #明文密码


    若已经安装BerkeleyDB数据库,则可把database设置为dbd形式,否则,可用ldbm 。(数据较慢)
    另外,密码可使用slappasswd 命令设定:

    引用
    # slappasswd -h {SHA} -s 'linuxfly.com'
    {SHA}vwSiF9lGtUkXixIIu/kFBDLc2Rg=


    并把配置文件修改为:

    引用
    rootpw {SHA}vwSiF9lGtUkXixIIu/kFBDLc2Rg=


    ※ 注意:slappasswd默认为{SSHA}方式密码,可使用-h 指定密码方式。

    启动即可:

    引用
    # service ldap start
    # netstat -ln|grep 389
    tcp        0      0 0.0.0.0:389                 0.0.0.0:*                   LISTEN
    tcp        0      0 :::389                      :::*                        LISTEN


    三、使用openldap-clients 套件操作ldap
    操作LDAP的方式有很多,下面以bash下用openldap-clients 套件提供的工具来进行。
    1、初始化数据
    这一步通常是由特定的应用程序根据其自身使用的需要创建的。包括两部分的内容:

    引用
    a、创建.schema文件,该文件定义了后面ldap 中存放对象的类型和属性,这已经在上面sldapd.conf文件中定义;
    b、初始化数据结构,其结构必须由上面的.schema文件已经定义的,并由应用程序读取和使用。


    初始化文件通常为.ldif 结尾,称为LDIF数据交换格式。这种格式是行界定、冒号分隔的属性-值对。例如:

    引用
    dn: dc=rfmail
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    dc: rfmail
    o: rfmail


    导入时,执行:

    # ldapadd -x -h 192.168.228.135 -D 'cn=root,dc=rfmail' -W -f rfmail.ldif


    ldapadd 命令各参数含义如下:

    引用
    -x 为使用简单密码验证方式
    -D 指定管理员DN(与slapd.conf中一致)
    -W 为管理员密码,可以使用-w password 直接输入密码
    -f 为初始化数据LDIF的文件名
    -h 为操作的服务器IP地址


    2、搜索操作
    LDAP是读优化数据库,因此,读的速度很快,也很常用。但与关系数据库不同,其以树结构形式读取数据,若不添加过滤,会显示匹配节点下所有节点的内容。若以ldif 的形式表达,刚开始可能不太习惯。

    # ldapsearch -x -b 'dc=rfmail'


    首先要留意的是,ldapsearch 不需要提供验证信息。因为正如前面提到的,LDAP 默认供任何人可读。
    -b 后面定义搜索节点位置,即从该节点往其子节点进行搜索

    再看下面的命令:

    # ldapsearch -x -b 'dc=rfmail' '(objectclass=top)'


    命令后面括号中定义的是filter,即过滤符。这里只有节点中有属性为objactclass=top的才显示。

    再看看:

    # ldapsearch -x -b 'dc=rfmail' '(objectclass=*)' 'dn'


    这行过滤符中用星号匹配,行末的'dn'表示仅显示该节点的dn属性,即requesting: dn。(否则会显示节点的全部属性)
    ※ 注意,请区分filter 与 requesting 的不同。前者为搜索匹配,后者为限制显示的属性值。

    3、身份验证
    修改或添加内容需进行用户验证,可通过下面的命令确认验证信息:

    引用
    # ldapwhoami -x -D 'cn=root,dc=rfmail' -w 'linuxfly.com'
    dn:cn=root,dc=rfmail
    Result: Success (0)


    4、修改操作
    修改内容通常由LDIF 文件提供。因此,可先用ldapsearch 导出节点内容:

    # ldapsearch -x -LLL -b 'dc=rfmail' '(objectclass=*)' 'dn' 'o' > example_dn.ldif


    -LLL 表示不输出注释内容,以便后续重新导入。
    用vi 修改.ldif 文件,内容改为:

    引用
    # cat example_dn.ldif
    dn: dc=rfmail
    o: linuxfly.com


    然后,执行下面的命令重新导入:

    # ldapmodify -x -D 'cn=root,dc=rfmail' -w 'linuxfly.com' -f example_dn.ldif


    查看结果:

    引用
    # ldapsearch -x -LLL -b 'dc=rfmail' '(objectclass=*)'
    dn: dc=rfmail
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    dc: rfmail
    o: linuxfly.com


    ※ 注意:

    引用
    a、ldapadd 与ldapmodify -a 作用相同
    b、如果在添加或修改时,报Naming violation等错误,则说明添加或修改的内容不符合schema中定义的对象属性规范,需修改后才能重新操作。


    5、删除操作
    删除时,给出DN即可:

    # ldapdelete -x -D 'cn=root,dc=rfmail' -w 'linuxfly.com' -r 'dc=rfmail'


    -r 表示以递归模式删除,即删除该节点下面的所有子节点。

    四、使用php操作LDAP
    用openldap-clients 操作LDAP可满足简单的查询、修改需要,但若需要更进一步的操作,建议用php、perl 等实现。下面以php 为例。
    1、绑定服务器

    1. $l_host="ldap://192.168.228.135"; //服务器IP  
    2. $l_port='389'; //LDAP服务端口  
    3. $l_loginpw='linuxfly.com'; //登陆密码,无论slapd.conf中是明文或{SHA}方式,这里都用明文  
    4. $l_logindn='cn=root,dc=rfmail'; //登陆DN  
    5. $l_root_dn='dc=rfmail'; //根目录 suffix  
    6. $l_conn=ldap_connect($l_host,$l_port) or die('Connect error!');  
    7. $boo=ldap_bind($l_conn,$l_logindn,$l_loginpw);  
    8. if ($boo)  
    9.     echo "成功绑定源服务器! ";  
    10.  else {  
    11.     echo "绑定源服务器失败 ";  
    12.     exit;  
    13. }  
    14. ldap_unbind($l_conn);  


    运行情况:

    引用
    # php ldap_test.php
    成功绑定源服务器!


    ※ 注意,在我的运行环境(php 5.1.6)中,虽然slapd.conf已设置为{SHA}方式,但在php中提供的登陆密码仍只需使用明文即可,而不要使用{SHA}的字符串形式。我在这里耽搁了很长时间,详细可见附录说明。

    2、读取操作
    在ldap_unbind()的前面增加如下语句:

    1. $l_filter='(objectClass=*)'; //设置过滤  
    2. $justthese=array('dn','o'); //设置输出属性  
    3. $search_id=ldap_search($l_conn,$l_root_dn,$l_filter,$justthese);  
    4. $l_entries = ldap_get_entries($l_conn,$search_id);  
    5. print_r($l_entries);  


    这个与ldap_search的使用基本一致的,ldap_get_entries()可返回由数组组成的全部匹配值的信息:

    引用
    # php ldap_test.php
    成功绑定源服务器!
    Array
    (
        [count] => 1
        [0] => Array
            (
                [o] => Array
                    (
                        [count] => 1
                        [0] => linuxfly.com
                    )

                [0] => o
                [count] => 1
                [dn] => dc=rfmail
            )

    )


    若匹配的数据很大,ldap_get_entries()返回的数组可能会超出内存限制。这时,可改用ldap_first_entry()方法:

    1. $search_id=ldap_search($l_conn,$l_root_dn,$l_filter,$justthese);  
    2. $l_entry = ldap_first_entry($l_conn,$search_id);  
    3. if ($l_entry) {  
    4.     do {  
    5.         $l_o = ldap_get_values($l_conn,$l_entry,'o');  
    6.         var_dump($l_entry,$l_o);  
    7.     } while ($l_entry=ldap_next_entry($l_conn,$l_entry));  
    8. }  


    结果:

    引用
    # php ldap_test.php
    成功绑定源服务器!
    resource(6) of type (ldap result entry)
    array(2) {
      [0]=>
      string(12) "linuxfly.com"
      ["count"]=>
      int(1)
    }


    3、修改操作
    与openldap-clients 套件提供的工具操作方式不同,PHP中对LDAP的修改和添加操作是分开的。用于修改的函数有两个ldap_modify和ldap_ mod_ replace,前者用于修改节点,后者用于修改属性。由于ldap_modify也可用于直接修改属性,故ldap_modify 较常用。
    这两个函数都只接受数组作为参数,表示新的属性值。

    1. $new_o=array('o'=>'linuxfly.org');  
    2. $boo = ldap_modify($l_conn,$l_root_dn,$new_o);  
    3. if (!$boo) {  
    4.     echo "Modify fail. ";  
    5. else {  
    6.     echo "Modify successfully. ";  
    7. }  


    运行结果:

    引用
    # php ldap_test.php
    成功绑定源服务器!
    Modify successfully.
    # ldapsearch -x -LLL -D cn=root,dc=rfmail -w linuxfly.com -b 'dc=rfmail'
    dn: dc=rfmail
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    dc: rfmail
    o: linuxfly.org


    4、添加操作
    同样的,添加操作也有两个函数,ldap_ add 和ldap_mod_add,前者用于添加节点,后者用于添加属性。但受限于schema规则限定,与修改操作不同,两个函数通常不能混用。
    a、添加一个新的节点
    代码:

    1. $add_entry=array();  
    2. $add_entry['dc']='linuxfly.com';  
    3. $add_entry['objectclass']='masterdomain';  
    4. $add_entry['kvcount']=0;  
    5. $new_dn='dc=linuxfly.com,dc=rfmail';  
    6. $boo = ldap_add($l_conn,$new_dn,$add_entry);  
    7. if (!$boo) {  
    8.     echo "Add fail. ";  
    9. else {  
    10.     echo "Add successfully. ";  
    11. }  


    ※ 使用ldap_add 时,需注意:

    引用
    1)需要用新添加的节点来定义新的DN值,并在后面的数组中有对应的值对;在上面,DN就是 dc=linuxfly.com,dc=rfmail 和$add_entry['dc']='linuxfly.com' 数组元素;
    2)objectclass 定义的对象不能缺少,写成$add_entry['objectClass']='masterdomain' 也可以。


    结果:

    引用
    # php ldap_test.php
    成功绑定源服务器!
    Add successfully.
    # ldapsearch -x -LLL -D cn=root,dc=rfmail -w linuxfly.com -b 'dc=rfmail'
    dn: dc=rfmail
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    dc: rfmail
    o: linuxfly.org

    dn: dc=linuxfly.com,dc=rfmail
    dc: linuxfly.com
    objectClass: masterdomain
    kvcount: 0


    b、给节点添加新属性
    LDAP中,同一个节点同一个属性是可以拥有多个值的。

    1. $add_entry2=array();  
    2. $add_entry2['operateUserList']=array('freeze','active');  
    3. $new_dn='dc=linuxfly.com,dc=rfmail';  
    4. $boo = ldap_mod_add($l_conn,$new_dn,$add_entry2);  
    5. if (!$boo) {  
    6.         echo "Add attributes values fail. ";  
    7. else {  
    8.         echo "Add attributest values successfully. ";  
    9. }  


    ※ 使用ldap_mod_add 时,需注意:

    引用
    1)不能用ldap_add代替,否则会报下面的错误
    ldap_add(): Add: Internal (implementation specific) error
    2)同一节点下,虽然可以有多个属性,但属性值不能重复,否则会报:
    ldap_mod_add(): Modify: Type or value exists
    (您可以再运行一次上面的脚本试试。)
    3)ldap_mod_add() 与ldap_mod_replace() 是不同的,前者添加,后者替换指定的属性值;不过,两函数都要求输入的数组元素值唯一,并且从0开始按顺序排列,否则,会报:
    ldap_mod_add(): Value array must have consecutive indices 0, 1, ...
    解决办法,见附录说明。


    结果:

    引用
    # php ldap_test.php
    成功绑定源服务器!
    Add attributes values successfully.
    # ldapsearch -x -LLL -D cn=root,dc=rfmail -w linuxfly.com -b 'dc=linuxfly.com,dc=rfmail'
    dn: dc=linuxfly.com,dc=rfmail
    dc: linuxfly.com
    objectClass: masterdomain
    kvcount: 0
    operateUserList: freeze
    operateUserList: active


    5、删除操作
    与添加类似,PHP对LDAP的删除操作可分为对节点和属性两个函数ldap_delete()、ldap_mod_del():

    1. $delete_attr['operateUserList']='freeze';  
    2. $delete_dn='dc=linuxfly.com,dc=rfmail';  
    3. $boo = ldap_mod_del($l_conn,$delete_dn,$delete_attr);  
    4. if (!$boo) {  
    5.     echo "Delete attributes values fail. ";  
    6. else {  
    7.     echo "Delete attributes values sucessfully. ";  
    8. }  
    9.   
    10. $boo = ldap_delete($l_conn,$delete_dn);  
    11. if (!$boo) {  
    12.     echo "Delete entry  fail. ";  
    13. else {  
    14.     echo "Delete entry  successfully. ";  
    15. }  


    ※ 注意:使用ldap_mod_del() 不能删除关键RDN及objectClass等属性。
    执行结果:

    引用
    # php ldap_test.php
    成功绑定源服务器!
    Delete attributes values sucessfully.
    # ldapsearch -x -LLL -D cn=root,dc=rfmail -w linuxfly.com -b 'dc=linuxfly.com,dc=rfmail'
    dn: dc=linuxfly.com,dc=rfmail
    dc: linuxfly.com
    objectClass: masterdomain
    kvcount: 0
    operateUserList: active
    # php ldap_test.php
    成功绑定源服务器!
    Delete entry  successfully.
    # ldapsearch -x -LLL -D cn=root,dc=rfmail -w linuxfly.com -b 'dc=rfmail'
    dn: dc=rfmail
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    dc: rfmail
    o: linuxfly.org


    五、附录
    1、SHA、SSHA等用户名密码问题
    slappasswd 命令可创建SHA、SSHA等结构的密码,默认为SSHA方式,创建方法可参考上面的描述。
    使用时,需分两种情况:

    引用
    a、虽然在sldapd.conf 的rootpw 部分设定该密码串,但在PHP bind()提供$passwd验证串时,只需提供明文形式的密码,而无需提供{SHA}等格式,否则验证会失败,报Invalid credentials 错误;
    b、当把{SHA}等方式用于普通属性值对时,PHP 需提供{SHA}形式的、完全一致的字符串,否则匹配将失败。


    根据PHP的不同版本,可用下面的形式获得{SHA}结构的密码串:

    1. $userpw="{SHA}".base64_encode(sha1('linuxfly.com',TRUE));  
    2. 或  
    3. $userpw="{SHA}".base64_encode(pack("H*",sha1('linuxfly.com')));  


    详情请参考:What are {SHA} and {SSHA} passwords and how do I generate them 一文。

    2、ldap_add() 、ldap_mod_add() 等函数可接受的数组问题
    ldap_add()、ldap_mod_add()、ldap_modify()、ldap_mod_repalce()等函数用于添加或修改节点、属性值对。在为它们提供新(或需修改)的属性时,必须以数组形式。而且,该数组元素必须唯一,而且从0序号开始逐一增加。类似下面的数组将不符合要求:

    1. $add_entry['uid']=array('1','0','1','2');  
    2. $mod_entry['uid']=array(1=>'1',3=>'2');  


    为满足要求,通常可使用array_unique() 和 array_slice()协助我们:

    1. $add_entry['uid']=array_unique($add_entry['uid']);  
    2. $mod_entry['uid']=array_slice($mod_entry['uid'],0); //重排序号  


    3、中文字符问题
    LDAP 中不允许保存GB2312或GB18030字符串,要保存这些字符集,必须用base64或unicode转码。PHP有提供base64_encode()和base64_decode()函数,unicode转码可参考网上资料。
    不过,LDAP可接受UTF-8 格式的中文字符。也就是说,您可以把GB2312或GB18030字符串,通过iconv()等函数转为UTF-8 后直接保存到LDAP中。

    1. $utf8_string=iconv('GBK','UTF-8//TRANSLIT',$string);  


    记住,当用ldapsearch输出LDAP内容时,UTF-8 编码的字符串将会自动以base64形式输出,需手动转码:

    引用
    # echo -n 'TGludXhGbHnpo5jmiaw='|base64 -d|iconv -f utf8 -t gb18030
    LinuxFly飘扬


    4、节点间关系
    LDAP没有冗余性或唯一性要求,各节点除了上下层(父子层)关系外,没有特殊的要求。
    因此,对于有特定要求的应用,例如,两个节点没有直接上下层关系,但某个节点属于另一个节点时(用户属于某个组)。这通常需利用LDAP节点可拥有多个属性值的特性,由程序来实现。例如,把用户的uid写入组节点的uid属性,多个用户属于该组,就在该组写入多个属性:

    引用
    uid: 1
    uid: 2
    uid: 3


    5、搜索匹配
    上面提供的ldapsearch或ldap_search()所使用的filter都很简单,实际上,该匹配方式有很多,例如:

    引用
    '(|(objectclass=user)(objectclass=person)' //表示或关系
    '(&(objectclass=user)(objectclass=person)' //表示和关系
    '(|(objectclass=user)(!(objectclass=person))' //表示或、非关系
    (&(|(objectclass=user)(objectclass=person)(objectclass=inetOrgPerson)
    (objectclass=organizationalPerson))(!(objectclass=computer))) //更复杂的类型


    请留意写法,更详细的内容,可参考:自定义 LDAP 过滤器和属性 和 LDAP 查询基本知识

    6、phpldapadmin工具
    有一个叫phpldapadmin的工具,其有点类似phpmyadmin,可提供图形化的管理LDAP。配置很简单,解压后,修改/etc/httpd/conf.d/phpldapadmin.conf 文件为:

    引用
    # cat phpldapadmin.conf
    Alias /phpldapadmin /usr/share/phpldapadmin/htdocs
    Alias /ldapadmin /usr/share/phpldapadmin/htdocs

    <Directory /usr/share/phpldapadmin/htdocs>
      Order Deny,Allow
      #Deny from all
      #Allow from 127.0.0.1
      Allow from all
    </Directory>


    然后启动Apache,访问http://ip/phpldapadmin ,输入登录DN和密码即可。需留意的是,EPEL上提供的phpldapadmin-1.0.1-1.el5.rpm 似乎有点问题,反正我是无法登陆成功,改用其他版本没有问题。
    另外,phpladpadmin受限于PHP的限制,若LDAP数据库很大,可能无法完全打开,并报内存不足的问题,故一般仅作简单使用。

    7、测试脚本
    上述操作的测试脚本:


    先配置好openldap,解压上述测试脚本包后,导入数据,并运行:

    # ldapadd -x -D 'cn=root,dc=rfmail' -w linuxfly.com -f example_init.ldif
    # php ldap_test.php


    六、参考资料
    LDAP Administration Guide
    (相当详细的介绍LDAP的online EBook)
    PHP Manual: Lightweight Directory Access Protocol
    OpenLDAP Faq-: What are {SHA} and {SSHA} passwords and how do I generate them 
    自定义 LDAP 过滤器和属性
    LDAP 查询基本知识
    OpenLDAP user - edit details and/or password
    (该文提供了一个SSHA格式密码的实现代码)
    OpenLDAP 2.2 Administrator's Guide: Using SASL
    openldap常用命令
    (该文只简略浏览了一下,没细看,但其中启用sasl 验证、配置服务器复制部分值得参考)
    使用 OpenLDAP 集中管理用户帐号
    (IBM 提供的一篇关于OpenLDAP 配置的文档,部分内容值得参考)
    使用 PHP 创建 LDAP 目录服务
    (这是无意中找到的一篇文章,描述用PHP操作Oracle Internet Directory ,没细看,但应该有值得参考的地方)

     

  • 相关阅读:
    PHP 5.5.0 Alpha5 发布
    Ubuntu Touch 只是另一个 Android 皮肤?
    MariaDB 10 已经为动态列提供文档说明
    Percona Toolkit 2.1.9 发布,MySQL 管理工具
    Oracle Linux 6.4 发布
    Ruby 2.0.0 首个稳定版本(p0)发布
    Apache Pig 0.11.0 发布,大规模数据分析
    Node.js 0.8.21 稳定版发布
    红薯 MySQL 5.5 和 5.6 默认参数值的差异
    Django 1.5 正式版发布,支持 Python 3
  • 原文地址:https://www.cnblogs.com/dwj97/p/7513509.html
Copyright © 2011-2022 走看看