openldap+php-ldap操作

https://www.cnblogs.com/dwj97/p/7513509.html

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 "成功綁定源服務器!\n";  
  10.  else {  
  11.     echo "綁定源服務器失敗\n";  
  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.\n";  
  5. } else {  
  6.     echo "Modify successfully.\n";  
  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.\n";  
  9. } else {  
  10.     echo "Add successfully.\n";  
  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.\n";  
  7. } else {  
  8.         echo "Add attributest values successfully.\n";  
  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.\n";  
  6. } else {  
  7.     echo "Delete attributes values sucessfully.\n";  
  8. }  
  9.   
  10. $boo = ldap_delete($l_conn,$delete_dn);  
  11. if (!$boo) {  
  12.     echo "Delete entry  fail.\n";  
  13. } else {  
  14.     echo "Delete entry  successfully.\n";  
  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數據庫很大,可能無法完全打開,並報內存不足的問題,故一般僅作簡單使用。

六、參考資料
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 ,沒細看,但應該有值得參考的地方)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章