码迷,mamicode.com
首页 > 其他好文 > 详细

LDAP目录服务折腾之后的总结

时间:2015-05-28 13:58:25      阅读:930      评论:0      收藏:0      [点我收藏+]

标签:

前言

公司管理员工信息以及组织架构的后台系统要和Active Directory目录服务系统打通,后台系统使用PHP开发,

折腾了二十多天,终于上线了,期间碰到过各种疑难问题,不过总算在GOOGLE大叔的帮忙下还有运维部AD管理员的帮助下解决了。

LDAP协议定义

LDAP(Lightweight Directory Access Protocol)轻量目录访问协议,定义了目录服务实现以及访问规范。

目录定义

A directory is a specialized database specifically designed for searching and browsing,
in additional to supporting basic lookup and update functions.

LDAP协议实现

0.基于TCP/IP的应用层协议 默认端口389 加密端口636
1.客户端发送命令,服务器端响应
2.目录主要操作
    2.0 用户验证(bind操作)
     2.1 添加节点
     2.2 更新节点
     2.3 移动节点
     2.4 删除节点
     2.5 节点搜索
3.节点类型
    3.0 节点属性规范(SCHEMA)
4.节点
    4.0 目录里的对象
     4.1 属性即是节点的数据
     4.2 目录中通过DN(Distinguished Name)唯一标识(可以认为是路径)
     4.2.0 节点DN = RDN(Relative Distinguished Name) + 父节点的DN
     4.3 目录是TREE结构,节点可以有子节点,也可以有父节点
5.属性
    5.0 同一个属性可以有多个值
     5.1 包含属性名称,属性类型
6.节点唯一标识DN说明
    6.0 示例: dn:CN=John Doe,OU=Texas,DC=example,DC=com
     6.1 从右到左 根节点 -> 子节点
     6.2 DC:所在控制域 OU:组织单元 CN:通用名称
7.目录规范(SCHEMA)
    7.0 目录节点相关规则
     7.1 Attribute Syntaxes
     7.2 Matching Rules
     7.3 Matching Rule Uses
     7.4 Attribute Types
     7.5 Object Classes
     7.6 Name Forms
     7.7 Content Rules
     7.8 Structure Rule

LDAP服务器端的实现

openLDAP,Active Directory(Microsoft)等等,除了实现协议之外的功能,还对它进行了扩展

LDAP应用场景

0.单点登录(用户管理)
1.局域网资源统一管理

封装的简单PHP类

适合AD服务器 其他的LDAP服务器需要做相应的修改

zend框架有个开源的LDAP库实现 完全面向对象

技术分享
  1 <?php
  2 /**
  3  * @description LDAP客户端类
  4  *
  5  * @author WadeYu
  6  * @date 2015-04-28
  7  * @version 0.0.1
  8  */
  9 class o_ldap{
 10     private $_conn = NULL;
 11     private $_sErrLog = ‘‘;
 12     private $_sOperLog = ‘‘;
 13     private $_aOptions = array(
 14         ‘host‘ => ‘ldap://xxx.com‘,
 15         ‘port‘ => ‘389‘,
 16         ‘dnSuffix‘ => ‘OU=xx,OU=xx,DC=xx,DC=com‘,
 17         ‘loginUser‘ => ‘‘,
 18         ‘loginPass‘ => ‘‘,
 19     );
 20     private $_aAllowAttrName = array(
 21         ‘objectClass‘,
 22         ‘objectGUID‘, //AD对象ID
 23         ‘userPassword‘, //AD密码不是这个字段 密码暂时不能通过程序设置
 24         ‘unicodePwd‘, //AD密码专用字段 $unicodePwd  = mb_convert_encoding(‘"‘ . $newPassword . ‘"‘, ‘utf-16le‘);
 25         ‘cn‘, //comman name 兄弟节点不能相同
 26         ‘ou‘, //organizationalUnit
 27         ‘description‘, //员工填工号
 28         ‘displayName‘, //中文名
 29         ‘name‘, //姓名
 30         ‘sAMAccountName‘, //英文名(RTX账号,唯一)
 31         ‘userPrincipalName‘, //登陆用户名 和 英文名一致
 32         ‘ProtectedFromAccidentalDeletion‘, //对象删除保护
 33         ‘givenName‘, //
 34         ‘sn‘, //
 35         ‘employeeNumber‘, //一卡通卡号
 36         ‘mail‘,
 37         ‘mailNickname‘,
 38         ‘manager‘, //上级 (节点路径 示例:CN=Texas Poker9,OU=Texas Poker,OU=Dept,OU=BoyaaSZ,DC=by,DC=com)
 39         ‘title‘, //头衔
 40         ‘pager‘, //性别 0男 1女 -1未知
 41         ‘userAccountControl‘, //用户账号策略(暂时不能设置) 资料说明地址:https://support.microsoft.com/en-gb/kb/305144
 42         ‘department‘,
 43         ‘managedBy‘,//部门负责人
 44         ‘distinguishedName‘,
 45         ‘pwdLastSet‘, //等于0时 下次登录时需要修改密码
 46     );
 47     
 48     public function __construct(array $aOptions = array()){
 49         if (!extension_loaded(‘ldap‘)){
 50             $this->_log(‘LDAP extension not be installed.‘,true);
 51         }
 52         $this->setOption($aOptions);
 53     }
 54     
 55     /**
 56      * @return exit || true
 57      */
 58     public function connect($force = false){
 59         if (!$this->_conn || $force){
 60             $host = $this->_aOptions[‘host‘];
 61             $port = $this->_aOptions[‘port‘];
 62             $this->_conn = ldap_connect($host,$port);
 63             if ($this->_conn === false){
 64                 $this->_log("Connect LDAP SERVER Failure.[host:{$post}:{$port}]",true);
 65             }
 66             ldap_set_option($this->_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
 67             ldap_set_option($this->_conn, LDAP_OPT_REFERRALS, 3);
 68             $this->_bind();
 69         }
 70         return true;
 71     }
 72     
 73     /**
 74      * @return exit || true
 75      */
 76     private function _bind(){
 77         $u = $this->_aOptions[‘loginUser‘];
 78         $p = $this->_aOptions[‘loginPass‘];
 79         $ret = @ldap_bind($this->_conn,$u,$p);
 80         if ($ret === false){
 81             $this->_log(__FUNCTION__.‘----‘.$this->_getLastExecErrLog().‘----‘."u:{$u},p:{$p}",true);
 82         }
 83         return $ret;
 84     }
 85     
 86     public function setOption(array $aOptions = array()){
 87         foreach($this->_aOptions as $k => $v){
 88             if (isset($aOptions[$k])){
 89                 $this->_aOptions[$k] = $aOptions[$k];
 90             }
 91         }
 92     }
 93     
 94     public function getOption($field,$default = ‘‘){
 95         return isset($this->_aOptions[$field]) ? $this->_aOptions[$field] : $default;
 96     }
 97     
 98     /**
 99      * @description 查询$dn下符合属性条件的节点 返回$limit条
100      *
101      * @return array [count:x,[[prop:[count:xx,[],[]]],....]]
102      */
103     public function getEntryList($dn,$aAttrFilter,array $aField=array(),$limit = 0,$bFixedDn = true){
104         if (!$dn = trim($dn)){
105             return array();
106         }
107         if (!$this->_checkDn($dn)){
108             return array();
109         }
110         $limit = max(0,intval($limit));
111         $this->connect();
112         if ($bFixedDn){
113             $dn = $this->_getFullDn($dn);
114         }
115         $aOldTmp = $aAttrFilter;
116         $this->_checkAttr($aAttrFilter);
117         if (!$aAttrFilter){
118             $this->_log(__FUNCTION__.‘---无效的搜索属性---‘.json_encode($aOldTmp));
119             return array();
120         }
121         $sAttrFilter = $this->_mkAttrFilter($aAttrFilter);
122         $attrOnly = 0;
123         $this->_log(__FUNCTION__."---DN:{$dn}---sAttr:{$sAttrFilter}",false,‘oper‘);
124         $rs = @ldap_search($this->_conn,$dn,$sAttrFilter,$aField,$attrOnly,$limit);
125         if ($rs === false){
126             $this->_log(__FUNCTION__."---dn:{$dn}---sAttr:{$sAttrFilter}---" . $this->_getLastExecErrLog());
127             return array();
128         }
129         $aRet = @ldap_get_entries($this->_conn,$rs);
130         ldap_free_result($rs);
131         if ($aRet === false){
132             $this->_log(__FUNCTION__.‘---‘.$this->_getLastExecErrLog());
133             return array();
134         }
135         return $aRet;
136     }
137     
138     /**
139      * @description 删除节点 暂时不考虑递归删除
140      * 
141      * @return boolean
142      */
143     public function delEntry($dn,$bFixedDn = true,$force = 0){
144         if (!$dn = trim($dn)){
145             return false;
146         }
147         if (!$this->_checkDn($dn)){
148             return false;
149         }
150         if ($bFixedDn){
151             $dn = $this->_getFullDn($dn);
152         }
153         $this->_log(__FUNCTION__."---DN:{$dn}",false,‘oper‘);
154         $this->connect();
155         /*if($force){
156             $aEntryList = $this->getEntryList($dn,array(‘objectClass‘=>‘*‘),array(‘objectClass‘));
157             if ($aEntryList && ($aEntryList[‘count‘] > 0)){ 
158                 for($i = 0; $i < $aEntryList[‘count‘]; $i++){
159                     $aDel[] = $aEntryList[$i][‘dn‘];
160                 }
161             }
162             $aDel = array_reverse($aDel); //默认顺序 祖先->子孙 需要先删除子孙节点
163             $ret = true;
164             foreach($aDel as $k => $v){
165                 $ret &= @ldap_delete($this->_conn,$v);
166             }
167             if ($ret === false){
168                 $this->_log(__FUNCTION__.‘dn(recursive):‘.$dn.‘----‘.$this->_getLastExecErrLog());
169             }
170             return $ret;
171         }*/
172         $ret = @ldap_delete($this->_conn,$dn);
173         if ($ret === false){
174             $this->_log(__FUNCTION__.‘----dn:‘.$dn.‘-----‘.$this->_getLastExecErrLog());
175         }
176         return $ret;
177     }
178     
179     /**
180      * @description 更新节点
181      * 
182      * @return boolean
183      */
184     public function updateEntry($dn,$aAttr = array(),$bFixedDn = true){
185         if (!$dn = trim($dn)){
186             return false;
187         }
188         $this->_checkAttr($aAttr);
189         if (!$aAttr){
190             return false;
191         }
192         if (!$this->_checkDn($dn)){
193             return false;
194         }
195         if ($bFixedDn){
196             $dn = $this->_getFullDn($dn);
197         }
198         $this->_log(__FUNCTION__."---DN:{$dn}---aAttr:".str_replace("\n",‘‘,var_export($aAttr,true)),false,‘oper‘);
199         $this->connect();
200         $ret = @ldap_modify($this->_conn,$dn,$aAttr);
201         if ($ret === false){
202             $this->_log(__FUNCTION__.‘---‘.$this->_getLastExecErrLog().‘---dn:‘.$dn.‘---attr:‘.json_encode($aAttr));
203         }
204         return $ret;
205     }
206     
207     /**
208      * @description 添加节点
209      *
210      * @return boolean
211      */
212     public function addEntry($dn,$aAttr = array(), $type = ‘employee‘/*employee,group*/){
213         if (!$dn = trim($dn)){
214             return false;
215         }
216         $this->_checkAttr($aAttr);
217         if (!$aAttr){
218             return false;
219         }
220         if (!$this->_checkDn($dn)){
221             return false;
222         }
223         $aAttr[‘objectClass‘] = (array)$this->_getObjectClass($type);
224         $this->_log(__FUNCTION__."---DN:{$dn}---aAttr:".str_replace("\n",‘‘,var_export($aAttr,true)),false,‘oper‘);
225         $this->connect();
226         $dn = $this->_getFullDn($dn);
227         $ret = @ldap_add($this->_conn,$dn,$aAttr);
228         if ($ret === false){
229             $this->_log(__FUNCTION__.‘----dn:‘.$dn.‘----aAttr:‘.json_encode($aAttr).‘-----‘.$this->_getLastExecErrLog());
230         }
231         return $ret;
232     }
233     
234     /**
235      * @description 移动叶子节点 v3版才支持此方法
236      *
237      * @param $newDn 相对于$parentDn
238      * @param $parentDn 完整DN
239      * @param $bMoveRecur 
240      *
241      * @return boolean
242      */
243     public function moveEntry($oldDn,$newDn,$parentDn,$bDelOld = true,$bFixDn = true,$bMoveRecur = true){
244         //对于AD服务器 此方法可以移动用户节点以及组织节点
245         //$newDn只能包含一个 比如OU=xxx
246         $oldDn = trim($oldDn);
247         $newDn = trim($newDn);
248         $parentDn = trim($parentDn);
249         if(!$oldDn || !$newDn || ($bFixDn && !$parentDn)){
250             return false;
251         }
252         if(!$this->_checkDn($oldDn) || !$this->_checkDn($newDn) || !$this->_checkDn($parentDn)){
253             return false;
254         }
255         $this->connect();
256         if($bFixDn){
257             $oldDn = $this->_getFullDn($oldDn);
258             $parentDn = $this->_getFullDn($parentDn);
259         }
260         $this->_log(__FUNCTION__."---DN:{$oldDn} -> {$newDn},{$parentDn}",false,‘oper‘);
261         $aTmpMove = $aDelDn = array();
262         $aTmpMove[] = array(‘old‘=>$oldDn,‘new‘=>$newDn);
263         /*if($bMoveRecur){
264             $aDelDn[] = $oldDn;
265             $aTmpList = $this->getEntryList($oldDn,array(‘objectClass‘=>‘*‘),array(‘objectClass‘),0,0);
266             if($aTmpList && ($aTmpList[‘count‘] > 1)){
267                 for($i = 1; $i < $aTmpList[‘count‘]; $i++){
268                     if(!in_array(‘user‘,$aTmpList[$i][‘objectclass‘])){ //$bDelOld=true时,用户节点移动时会自动删除
269                         $aDelDn[] = $aTmpList[$i][‘dn‘];
270                     }
271                     $aTmpSep = explode($oldDn,$aTmpList[$i][‘dn‘]);
272                     $aTmpMove[] = array(
273                         ‘old‘ => $aTmpList[$i][‘dn‘],
274                         ‘new‘ => $aTmpSep[0] . $newDn,
275                     );
276                 }
277             }
278         }*/
279         $bFlag = true;
280         foreach($aTmpMove as $k => $v){
281             $bTmpFlag = ldap_rename($this->_conn,$v[‘old‘],$v[‘new‘],$parentDn,(boolean)$bDelOld);
282             if(!$bTmpFlag){
283                 $this->_log(__FUNCTION__."---o:{$v[‘old‘]}-n:{$v[‘new‘]}-p:{$parentDn}-recur:{$bMoveRecur}-----".$this->_getLastExecErrLog());
284             }
285             $bFlag &= $bTmpFlag;
286         }
287         /*if(!$bFlag){
288             $this->_log(__FUNCTION__."---o:{$oldDn}-n:{$newDn}-p:{$parentDn}-recur:{$bMoveRecur}-----".$this->_getLastExecErrLog());
289         }*/
290         /*if($bFlag && $bDelOld && $aDelDn){
291             $aDelDn = array_reverse($aDelDn);
292             foreach($aDelDn as $k => $v){
293                 $this->delEntry($v,false);
294             }
295         }*/
296         return $bFlag;
297     }
298     
299     public function modEntry($dn,$act = ‘add‘,$aAttr = array()){
300         return false;
301         $dn = $this->_getFullDn($dn);
302         $this->_log(__FUNCTION__."---DN:{$dn}---Act:{$act}---aAttr:".str_replace("\n",‘‘,var_export($aAttr,true)),false,‘oper‘);
303         $this->connect();
304         $ret = false;
305         switch($act){
306             case ‘add‘: $ret = ldap_mod_add($this->_conn,$dn,$aAttr); break;
307             case ‘replace‘: $ret = ldap_mod_replace($this->_conn,$dn,$aAttr); break;
308             case ‘del‘: $ret = ldap_mod_del($this->_conn,$dn,$aAttr); break;
309         }
310         if(!$ret){
311             $this->_log(__FUNCTION__."---dn:{$dn}---act:{$act}---attr:".json_encode($aAttr).‘---‘.$this->_getLastExecErrLog());
312         }
313         return $ret;
314     }
315     
316     /**
317      * @description 批量添加节点
318      * 
319      * @return boolean
320      */
321     public function addBatchEntry($aNodeList = array()){
322     }
323     
324     public function getAttrKv(array $aAttr = array()){
325         if(!isset($aAttr[‘count‘]) || ($aAttr[‘count‘] < 1)){
326             return array();
327         }
328         $aRet = array();
329         for($i = 0; $i < $aAttr[‘count‘]; $i++){
330             $field = $aAttr[$i];
331             if (!isset($aAttr[$field])){
332                 return array();
333             }
334             unset($aAttr[$field][‘count‘]);
335             $aRet[$field] = $aAttr[$field];
336         }
337         if(isset($aAttr[‘dn‘])){ //dn是字符串
338             $aRet[‘dn‘] = $aAttr[‘dn‘];
339         }
340         return $aRet;
341     }
342     
343     private function _getObjectClass($type = ‘employee‘){
344         $aRet = array();
345         switch($type){
346             case ‘employee‘ : $aRet = array(‘top‘,‘person‘,‘organizationalPerson‘,‘user‘); break;
347             case ‘group‘ : $aRet = array(‘top‘,‘organizationalUnit‘); break;
348         }
349         return $aRet;
350     }
351     
352     public function getFullDn($partDn = ‘‘){
353         return $this->_getFullDn($partDn);
354     }
355     
356     private function _getFullDn($partDn = ‘‘){
357         $partDn = trim($partDn);
358         $partDn = rtrim($partDn,‘,‘);
359         return "{$partDn},{$this->_aOptions[‘dnSuffix‘]}";
360     }
361     
362     private function _checkDn($dn = ‘‘){
363         $dn = trim($dn,‘,‘);
364         $aDn = explode(‘,‘,$dn);
365         foreach($aDn as $k => $v){
366             $aTmp = explode(‘=‘,$v);
367             $aTmp[0] = strtolower(trim($aTmp[0]));
368             $aTmp[1] = trim($aTmp[1]);
369             $flag = false;
370             switch($aTmp[0]){ //distingushed name 暂时只允许这3个field
371                 case ‘dc‘: $flag = $this->_checkDc($aTmp[1]); break;
372                 case ‘ou‘: $flag = $this->_checkOu($aTmp[1]); break;
373                 case ‘cn‘: $flag = $this->_checkCn($aTmp[1]); break;
374             }
375             if (!$flag){
376                 $this->_log(__FUNCTION__.‘----无效的节点路径----dn:‘.$dn);
377                 return false;
378             }
379         }
380         return true;
381     }
382     
383     private function _checkOu($ou = ‘‘){
384         if (!$ou){
385             return false;
386         }
387         if (preg_match(‘/[^a-zA-Z\s\d\.&\‘\d]/‘,$ou)){
388             $this->_log(__FUNCTION__.‘----OU只能包含字母数字空格以及点‘);
389             return false;
390         }
391         return true;
392     }
393     
394     private function _checkCn($cn = ‘‘){
395         if (!$cn){
396             return false;
397         }
398         return true;
399     }
400     
401     private function _checkDc($dc = ‘‘){
402         if (!$dc){
403             return false;
404         }
405         if (preg_match(‘/[^a-zA-Z]/‘,$dc)){
406             $this->_log(__FUNCTION__.‘----DC只能包含英文字母‘);
407             return false;
408         }
409         return true;
410     }
411     
412     private function _mkAttrFilter(array $aAttrFilter = array()){
413         $sStr = ‘(&‘;
414         foreach($aAttrFilter as $k => $v){
415             $v = (string)$v;
416             if($k === ‘objectGUID‘){
417                 $v = $this->_GUIDtoStr($v);
418             }
419             $v = addcslashes($v,‘()=‘);
420             $sStr .= "({$k}={$v})";
421         }
422         $sStr .= ‘)‘;
423         return $sStr;
424     }
425     
426     //来自PHP.NET http://php.net/manual/en/function.ldap-search.php
427     //http://php.net/manual/en/function.ldap-get-values-len.php
428     //GUID关键字
429     private function _GUIDtoStr($binary_guid){
430         $hex_guid = unpack("H*hex", $binary_guid); 
431         $hex = $hex_guid["hex"];
432         $j = 0;$str = ‘\\‘;
433         for($i = 0; $i < strlen($hex); $i++){
434             if($j == 2){
435                 $str .= ‘\\‘;
436                 $j = 0;
437             }
438             $str .= $hex[$i];
439             $j++;
440         }
441         return $str;
442         /*$hex1 = substr($hex, -26, 2) . substr($hex, -28, 2) . substr($hex, -30, 2) . substr($hex, -32, 2);
443         $hex2 = substr($hex, -22, 2) . substr($hex, -24, 2);
444         $hex3 = substr($hex, -18, 2) . substr($hex, -20, 2);
445         $hex4 = substr($hex, -16, 4);
446         $hex5 = substr($hex, -12, 12);
447         $guid_str = $hex1 . "-" . $hex2 . "-" . $hex3 . "-" . $hex4 . "-" . $hex5;
448         return $guid_str;*/
449     }
450     
451     private function _checkAttr(& $aAttr = array()){
452         foreach((array)$aAttr as $k => $v){
453             if (!in_array($k,$this->_aAllowAttrName)){
454                 unset($aAttr[$k]);
455             }
456         }
457         return true;
458     }
459     
460     public function getErrLog(){
461         return $this->_sErrLog;
462     }
463     
464     public function getOperLog(){
465         return $this->_sOperLog;
466     }
467     
468     private function _log($str = ‘‘,$bExit = false,$type = ‘err‘/*err,oper*/){
469         if ($bExit){
470             die($str);
471         }
472         $date = date(‘Y-m-d H:i:s‘);
473         if($type === ‘err‘){
474             $this->_sErrLog .= "{$date}----{$str}\n";
475         } else if ($type === ‘oper‘){
476             $this->_sOperLog .= "{$date}----{$str}\n";
477         }
478     }
479     
480     public function close(){
481         ldap_close($this->_conn);
482     }
483     
484     private function _getLastExecErrLog(){
485         $no = ldap_errno($this->_conn);
486         $err = ldap_error($this->_conn);
487         return "---exec Error:{$no}---{$err}";
488     }
489 }
View Code

辅助PHP类---汉字转拼音

<?php
/**
 * @desc 汉字转拼音 参考 http://www.php100.com/html/webkaifa/PHP/PHP/2012/0820/10914.html
 */
class o_lib_helper_pinyin{
	private $_DataKey = "a|ai|an|ang|ao|ba|bai|ban|bang|bao|bei|ben|beng|bi|bian|biao|bie|bin|bing|bo|bu|ca|cai|can|cang|cao|ce|ceng|cha
		|chai|chan|chang|chao|che|chen|cheng|chi|chong|chou|chu|chuai|chuan|chuang|chui|chun|chuo|ci|cong|cou|cu|
		cuan|cui|cun|cuo|da|dai|dan|dang|dao|de|deng|di|dian|diao|die|ding|diu|dong|dou|du|duan|dui|dun|duo|e|en|er
		|fa|fan|fang|fei|fen|feng|fo|fou|fu|ga|gai|gan|gang|gao|ge|gei|gen|geng|gong|gou|gu|gua|guai|guan|guang|gui
		|gun|guo|ha|hai|han|hang|hao|he|hei|hen|heng|hong|hou|hu|hua|huai|huan|huang|hui|hun|huo|ji|jia|jian|jiang
		|jiao|jie|jin|jing|jiong|jiu|ju|juan|jue|jun|ka|kai|kan|kang|kao|ke|ken|keng|kong|kou|ku|kua|kuai|kuan|kuang
		|kui|kun|kuo|la|lai|lan|lang|lao|le|lei|leng|li|lia|lian|liang|liao|lie|lin|ling|liu|long|lou|lu|lv|luan|lue
		|lun|luo|ma|mai|man|mang|mao|me|mei|men|meng|mi|mian|miao|mie|min|ming|miu|mo|mou|mu|na|nai|nan|nang|nao|ne
		|nei|nen|neng|ni|nian|niang|niao|nie|nin|ning|niu|nong|nu|nv|nuan|nue|nuo|o|ou|pa|pai|pan|pang|pao|pei|pen
		|peng|pi|pian|piao|pie|pin|ping|po|pu|qi|qia|qian|qiang|qiao|qie|qin|qing|qiong|qiu|qu|quan|que|qun|ran|rang
		|rao|re|ren|reng|ri|rong|rou|ru|ruan|rui|run|ruo|sa|sai|san|sang|sao|se|sen|seng|sha|shai|shan|shang|shao|
		she|shen|sheng|shi|shou|shu|shua|shuai|shuan|shuang|shui|shun|shuo|si|song|sou|su|suan|sui|sun|suo|ta|tai|
		tan|tang|tao|te|teng|ti|tian|tiao|tie|ting|tong|tou|tu|tuan|tui|tun|tuo|wa|wai|wan|wang|wei|wen|weng|wo|wu
		|xi|xia|xian|xiang|xiao|xie|xin|xing|xiong|xiu|xu|xuan|xue|xun|ya|yan|yang|yao|ye|yi|yin|ying|yo|yong|you
		|yu|yuan|yue|yun|za|zai|zan|zang|zao|ze|zei|zen|zeng|zha|zhai|zhan|zhang|zhao|zhe|zhen|zheng|zhi|zhong|
		zhou|zhu|zhua|zhuai|zhuan|zhuang|zhui|zhun|zhuo|zi|zong|zou|zu|zuan|zui|zun|zuo";
	private $_DataValue = "-20319|-20317|-20304|-20295|-20292|-20283|-20265|-20257|-20242|-20230|-20051|-20036|-20032|-20026|-20002|-19990
		|-19986|-19982|-19976|-19805|-19784|-19775|-19774|-19763|-19756|-19751|-19746|-19741|-19739|-19728|-19725
		|-19715|-19540|-19531|-19525|-19515|-19500|-19484|-19479|-19467|-19289|-19288|-19281|-19275|-19270|-19263
		|-19261|-19249|-19243|-19242|-19238|-19235|-19227|-19224|-19218|-19212|-19038|-19023|-19018|-19006|-19003
		|-18996|-18977|-18961|-18952|-18783|-18774|-18773|-18763|-18756|-18741|-18735|-18731|-18722|-18710|-18697
		|-18696|-18526|-18518|-18501|-18490|-18478|-18463|-18448|-18447|-18446|-18239|-18237|-18231|-18220|-18211
		|-18201|-18184|-18183|-18181|-18012|-17997|-17988|-17970|-17964|-17961|-17950|-17947|-17931|-17928|-17922
		|-17759|-17752|-17733|-17730|-17721|-17703|-17701|-17697|-17692|-17683|-17676|-17496|-17487|-17482|-17468
		|-17454|-17433|-17427|-17417|-17202|-17185|-16983|-16970|-16942|-16915|-16733|-16708|-16706|-16689|-16664
		|-16657|-16647|-16474|-16470|-16465|-16459|-16452|-16448|-16433|-16429|-16427|-16423|-16419|-16412|-16407
		|-16403|-16401|-16393|-16220|-16216|-16212|-16205|-16202|-16187|-16180|-16171|-16169|-16158|-16155|-15959
		|-15958|-15944|-15933|-15920|-15915|-15903|-15889|-15878|-15707|-15701|-15681|-15667|-15661|-15659|-15652
		|-15640|-15631|-15625|-15454|-15448|-15436|-15435|-15419|-15416|-15408|-15394|-15385|-15377|-15375|-15369
		|-15363|-15362|-15183|-15180|-15165|-15158|-15153|-15150|-15149|-15144|-15143|-15141|-15140|-15139|-15128
		|-15121|-15119|-15117|-15110|-15109|-14941|-14937|-14933|-14930|-14929|-14928|-14926|-14922|-14921|-14914
		|-14908|-14902|-14894|-14889|-14882|-14873|-14871|-14857|-14678|-14674|-14670|-14668|-14663|-14654|-14645
		|-14630|-14594|-14429|-14407|-14399|-14384|-14379|-14368|-14355|-14353|-14345|-14170|-14159|-14151|-14149
		|-14145|-14140|-14137|-14135|-14125|-14123|-14122|-14112|-14109|-14099|-14097|-14094|-14092|-14090|-14087
		|-14083|-13917|-13914|-13910|-13907|-13906|-13905|-13896|-13894|-13878|-13870|-13859|-13847|-13831|-13658
		|-13611|-13601|-13406|-13404|-13400|-13398|-13395|-13391|-13387|-13383|-13367|-13359|-13356|-13343|-13340
		|-13329|-13326|-13318|-13147|-13138|-13120|-13107|-13096|-13095|-13091|-13076|-13068|-13063|-13060|-12888
		|-12875|-12871|-12860|-12858|-12852|-12849|-12838|-12831|-12829|-12812|-12802|-12607|-12597|-12594|-12585
		|-12556|-12359|-12346|-12320|-12300|-12120|-12099|-12089|-12074|-12067|-12058|-12039|-11867|-11861|-11847
		|-11831|-11798|-11781|-11604|-11589|-11536|-11358|-11340|-11339|-11324|-11303|-11097|-11077|-11067|-11055
		|-11052|-11045|-11041|-11038|-11024|-11020|-11019|-11018|-11014|-10838|-10832|-10815|-10800|-10790|-10780
		|-10764|-10587|-10544|-10533|-10519|-10331|-10329|-10328|-10322|-10315|-10309|-10307|-10296|-10281|-10274
		|-10270|-10262|-10260|-10256|-10254";
	private $_Data = array();
	
	public function __construct(){
		$_TDataKey = explode(‘|‘, str_replace(array("\t","\n","\r\n",‘ ‘),array(‘‘,‘‘,‘‘,‘‘),$this->_DataKey));
		$_TDataValue = explode(‘|‘, str_replace(array("\t","\n","\r\n",‘ ‘),array(‘‘,‘‘,‘‘,‘‘),$this->_DataValue));
		$_Data = (PHP_VERSION>=‘5.0‘) ? array_combine($_TDataKey, $_TDataValue) : $this->_Array_Combine($_TDataKey, $_TDataValue);
		arsort($_Data);
		$this->_Data = $_Data;
	}
	
	public function Pinyin($_String, $_Code=‘gb2312‘){
		reset($this->_Data);
		if($_Code != ‘gb2312‘) $_String = $this->_U2_Utf8_Gb($_String);
		$_Res = ‘‘;$_aRes = array();$_en = ‘‘;
		for($i=0; $i<strlen($_String); $i++)
		{
			$_P = ord(substr($_String, $i, 1));
			if($_P < 160){
				$_en .= chr($_P);
				continue;
			}
			if($_en){
				$_aRes[] = $_en;
				$_en = ‘‘;
			}
			if($_P>160) { $_Q = ord(substr($_String, ++$i, 1)); $_P = $_P*256 + $_Q - 65536; }
			//$_Res .= _Pinyin($_P, $_Data);
			$_aRes[] = $this->_Pinyin($_P, $this->_Data);
		}
		foreach($_aRes as $k => $v){
			$v = preg_replace("/[^a-zA-Z0-9]*/", ‘‘, $v);
			$v = ucfirst($v);
			$_aRes[$k] = $v;
		}
		return implode(‘ ‘,$_aRes);
		//return preg_replace("/[^a-zA-Z0-9]*/", ‘‘, $_Res);
	}
	
	private function _Pinyin($_Num, $_Data){
		if ($_Num>0 && $_Num<160 ) return chr($_Num);
		elseif($_Num<-20319 || $_Num>-10247) return ‘‘;
		else {
		foreach($_Data as $k=>$v){ if($v<=$_Num) break; }
		return $k;
		}
	}
	
	private function _U2_Utf8_Gb($_C){
		$_String = ‘‘;
		if($_C < 0x80) $_String .= $_C;
		elseif($_C < 0x800)
		{
			$_String .= chr(0xC0 | $_C>>6);
			$_String .= chr(0x80 | $_C & 0x3F);
		}elseif($_C < 0x10000){
			$_String .= chr(0xE0 | $_C>>12);
			$_String .= chr(0x80 | $_C>>6 & 0x3F);
			$_String .= chr(0x80 | $_C & 0x3F);
		} elseif($_C < 0x200000) {
			$_String .= chr(0xF0 | $_C>>18);
			$_String .= chr(0x80 | $_C>>12 & 0x3F);
			$_String .= chr(0x80 | $_C>>6 & 0x3F);
			$_String .= chr(0x80 | $_C & 0x3F);
		}
		return iconv(‘UTF-8‘, ‘GB2312‘, $_String);
	}
	
	private function _Array_Combine($_Arr1, $_Arr2){
		for($i=0; $i<count($_Arr1); $i++) $_Res[$_Arr1[$i]] = $_Arr2[$i];
		return $_Res;
	}
}

辅助PHP类---简单TREE处理

<?php
/**
 * @description 简单tree类
 *
 * @author WadeYu
 * @date 2015-04-30
 * @version 0.0.1
 */
class o_simpletree{
	private $_aNodeList = array(); // [id => []]
	private $_aChildNodeList = array(); // [parentId => [childId,childId,]]
	private $_aTopNodeList = array(); //[id => []]
	private $_pk = ‘‘;
	private $_parentPk = ‘‘;
	private $_aTreeKV = array(); //tree key的显示的内容
	private $_aJoinKey = array();
	
	public function __construct(array $aData = array()/*[[id:0,fid:1],[]]*/, array $aOption = array()){
		$this->_pk = $aOption[‘pk‘];
		$this->_parentPk = $aOption[‘parentPk‘];
		$this->_aTreeKV = (array)$aOption[‘aTreeKV‘];
		$this->_mkNodeList($aData);
		$this->_mkChildNodeList();
	}
	
	public function getTree($parentPk){
		$aRet = array();
		$aChild = array();
		if (!isset($this->_aChildNodeList[$parentPk])){
			return $aRet;
		}
		$aChild = $this->_aChildNodeList[$parentPk];
		foreach((array)$aChild as $k => $v){
			$currNode = $this->_aNodeList[$v];
			$tmpK = ‘‘;
			$i = 0;
			foreach($this->_aTreeKV as $k2 => $v2){
				$tmpV = $currNode[$v2];
				if($i == 0){
					$tmpK .= $tmpV;
				} else if ($i == 1){
					$tmpK .= "({$tmpV})";
				} else if ($i == 2){
					$tmpK .= "[{$tmpV}]";
				}
				$i++;
			}
			if (isset($this->_aChildNodeList[$v])){
				$aRet[$tmpK] = $this->getTree($v);
			} else {
				$aRet[$tmpK] = 1;
			}
		}
		return $aRet;
	}
	
	public function joinKey($aTree,$prefix = ‘‘){
		$prefix = trim($prefix);
		if(!is_array($aTree)){
			return $prefix;
		}
		foreach((array)$aTree as $k => $v){
			if (is_array($v)){
				$this->joinKey($v,"{$prefix}{$k}/");
			} else {
				$this->_aJoinKey["{$prefix}{$k}"] = 1;
			}
		}
		return true;
	}
	
	public function getJoinKey(){
		return $this->_aJoinKey;
	}
	
	private function _mkNodeList(array $aData = array()){
		foreach($aData as $k => $v){
			$this->_aNodeList[$v[$this->_pk]] = $v;
		}
	}
	
	private function _mkChildNodeList(){
		foreach($this->_aNodeList as $k => $v){
			if ($v[‘fid‘]){
				$this->_aChildNodeList[$v[$this->_parentPk]][] = $v[$this->_pk];
			} else {
				$this->_aTopNodeList[$v[$this->_pk]] = $v;
			}
		}
	}
}

  

 

 

LDAP开源实现:openLDAP

0.相关环境说明
    a.操作系统

1 [root@vm ldap]# lsb_release -a
2 LSB Version:    :core-3.1-amd64:core-3.1-ia32:core-3.1-noarch:graphics-3.1-amd64:graphics-3.1-ia32:graphics-3.1-noarch
3 Distributor ID:    CentOS
4 Description:    CentOS release 5.4 (Final)
5 Release:    5.4
6 Codename:    Final

 1.安装

    a.yum -y install openldap-servers openldap-clients

2.配置
    a.配置HOST
        [root@vm ldap]# vi /etc/hosts
       127.0.0.1 test.com
     b.创建证书

cd /etc/pki/tls/certs
[root@vm certs]# pwd
/etc/pki/tls/certs
[root@vm certs]# rm -rf slapd.pem
[root@vm certs]# make slapd.pem
#执行命令之后 显示如下信息 按照提示填写即可
umask 77 ; 	PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; 	PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; 	/usr/bin/openssl req -utf8 -newkey rsa:2048 -keyout $PEM1 -nodes -x509 -days 365 -out $PEM2 -set_serial 0 ; 	cat $PEM1 >  slapd.pem ; 	echo ""    >> slapd.pem ; 	cat $PEM2 >> slapd.pem ; 	rm -f $PEM1 $PEM2
Generating a 2048 bit RSA private key
.....................................+++
.........+++
writing new private key to ‘/tmp/openssl.IQ8972‘
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.‘, the field will be left blank.
-----
Country Name (2 letter code) [GB]:CN
State or Province Name (full name) [Berkshire]:GuangDong
Locality Name (eg, city) [Newbury]:ShenZhen
Organization Name (eg, company) [My Company Ltd]:Boyaa
Organizational Unit Name (eg, section) []:OA
Common Name (eg, your name or your server‘s hostname) []:Test LDAP  
Email Address []:WadeYu@boyaa.com
[root@vm certs]#

   c.生成管理员密码

            root@vm ~]# slappasswd 

            New password:
           Re-enter new password:
          {SSHA}2eG1IBeHhSjfgS7pjoAci1bHz5p4AVeS

    d.配置slapd.conf
        [root@vm certs]# vi /etc/openldap/slapd.conf

       去掉TLS相关注释

技术分享

       设置数据库配置

技术分享

    e.BerkeleyDb配置

        [root@vm certs]# cd /etc/openldap/
        [root@vm openldap]# mv ./DB_CONFIG.example /var/lib/ldap/DB_CONFIG

    f.配置ldap.conf

        [root@vm openldap]# vi ldap.conf

技术分享

    g.开启加密支持
        [root@vm ~]# vim /etc/sysconfig/ldap
        SLAPD_LDAPS=yes

3.使用slapadd命令添加根节点 未启动前

1 [root@vm ~]# cd ~
2 [root@vm ~]# vim root.ldif
3 dn: dc=test,dc=com
4 dc: test
5 objectClass: dcObject
6 objectClass: organizationalUnit
7 ou: test.com
8 [root@vm ~]# slapadd -v -n 1 -l root.ldif

4.启动slapd
[root@vm ~]# slapd -h "ldap:// ldaps://"
389非加密端口 636加密端口

技术分享

5.检测

[root@vm ~]# ldapsearch -x -H ldap://localhost
# extended LDIF
#
# LDAPv3
# base <> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# test.com
dn: dc=test,dc=com
dc: test
objectClass: dcObject
objectClass: organizationalUnit
ou: test.com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

[root@vm ~]# ldapsearch -x -H ldaps://localhost
# extended LDIF
#
# LDAPv3
# base <> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# test.com
dn: dc=test,dc=com
dc: test
objectClass: dcObject
objectClass: organizationalUnit
ou: test.com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

  

6.phpldapadmin客户端访问
    a.官网下载源码放入WEB目录下 下载页面:http://phpldapadmin.sourceforge.net/wiki/index.php/Download
    b.安装依赖的扩展gettext ldap这2个扩展
    c.按需配置 源码目录下config/config.php

技术分享

问题

1.ldaps无法访问
/etc/openldap/ldap.conf TLS_REQCERT never加上这个

源码安装PHP扩展步骤

1.下载PHP源码
2.切换到扩展源码所在目录 示例:cd /src path/ext/extname
3.执行phpize命令 (1.PHP源代码或者PHP安装目录包含此命令 2.当前目录会生成一个检查编译扩展的环境脚本configure)
4../configure --with-php-config=/usr/local/php/bin/php-config (1.编译环境配置检查 2.生成make命令需要的编译配置文件makefile)
5.make && make install (编译安装)
6.php.ini 加上配置 extension = "xxxxx.so"
7.重启php service php-fpm restart

安装PHP LDAP扩展

示例:centos系统安装LDAP扩展
1.安装依赖库openldap openldap-devel
yum install openldap
yum install openldap-devel
2.参考:源码安装PHP扩展步骤

 

参考资料

[1] LDAPV3协议

http://tools.ietf.org/html/rfc4511

[2] LDAP百度百科

http://baike.baidu.com/view/159263.htm

[3] WIKI LDAP

http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol

[4] LDAP Data Interchange Format

http://en.wikipedia.org/wiki/LDAP_Data_Interchange_Format

[5] OpenLDAP介绍

http://en.wikipedia.org/wiki/OpenLDAP

[6] OpenLDAP管理员文档

[7] Zend LDAP API

http://framework.zend.com/manual/1.11/en/zend.ldap.api.html

[8] BerkeleyDb以及OPENLDAP安装指南

http://www.openldap.org/lists/openldap-technical/201001/msg00046.html

[9] LDAP环境搭建 OpenLDAP和phpLDAPadmin -- yum版

http://www.cnblogs.com/yafei236/p/4141897.html

[10] phpldapadmin开源项目

http://phpldapadmin.sourceforge.net/wiki/index.php/Main_Page

LDAP目录服务折腾之后的总结

标签:

原文地址:http://www.cnblogs.com/wadeyu/p/ldap-search-summary.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!