标签:
公司管理员工信息以及组织架构的后台系统要和Active Directory目录服务系统打通,后台系统使用PHP开发,
折腾了二十多天,终于上线了,期间碰到过各种疑难问题,不过总算在GOOGLE大叔的帮忙下还有运维部AD管理员的帮助下解决了。
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.
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
openLDAP,Active Directory(Microsoft)等等,除了实现协议之外的功能,还对它进行了扩展
0.单点登录(用户管理)
1.局域网资源统一管理
适合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 }
辅助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; } } } }
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加上这个
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
示例: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
标签:
原文地址:http://www.cnblogs.com/wadeyu/p/ldap-search-summary.html