我很难查询微软的Active Directory并遇到一些困难:
每个请求的AD大小限制为1000个元素。我不能改变大小限制。 PHP似乎不支持分页(我使用的是5.2版,而且无法更新生产服务器。)
到目前为止,我遇到了两种可能的解决方案:
所以我的问题:
这里最好使用哪种方法?
如果是第一个,我怎样才能确保正确处理objectSid?
还有其他可能吗? 我错过了一些明显的东西吗?
更新
- This相关问题提供有关简单分页结果扩展无效的原因的信息
- Web服务器在Linux服务器上运行,因此COM对象/ adoDB不是一个选项。
答案 0 :(得分:1)
永远不要对服务器或服务器配置做出假设,这会导致代码脆弱,出现意外,有时甚至是惊人的失败。仅仅因为它今天是AD并不意味着它将是明天,或者微软不会改变服务器中的默认限制。我最近处理的情况是客户端代码是用部落知识编写的,大小限制是2000,当管理员出于自己的原因改变了大小限制时,客户端代码失败了。
您确定PHP不支持请求控件(简单的分页结果扩展是请求控件)吗?我写了一篇关于"LDAP: Simple Paged Results"的文章,虽然文章示例代码是Java,但概念很重要,而不是语言。另请参阅"LDAP: Programming Practices"。
答案 1 :(得分:1)
由于我没有找到任何干净的解决方案,我决定采用第一种方法:按对象方式过滤。
这种解决方法有其局限性:
这个想法是首先读取所有可能的对象并选择具有最低相对SID的对象。相对SID是SID中的最后一个块:
S-1-5-21-3188256696-111411151-3922474875- 1158
假设这是仅返回“部分搜索结果”的搜索中的最低相对SID。 让我们进一步假设大小限制是1000。
该程序然后执行以下操作: 它使用
之间的SID搜索所有对象 S-1-5-21-3188256696-111411151-3922474875-的 1158 强>
和
S-1-5-21-3188256696-111411151-3922474875- 0159
然后全部在
之间 S-1-5-21-3188256696-111411151-3922474875-的 1158 强>
和
S-1-5-21-3188256696-111411151-3922474875-的 2157 强>
等等,直到其中一个搜索返回零对象。
这种方法存在一些问题,但这对我的目的来说已经足够了 守则:
$filter = '(objectClass=Group)';
$attributes = array('objectsid','cn'); //objectsid needs to be set
$result = array();
$maxPageSize = 1000;
$searchStep = $maxPageSize-1;
$adResult = @$adConn->search($filter,$attributes); //Supress warning for first query (because it exceeds the size limit)
//Read smallest RID from the resultset
$minGroupRID = '';
for($i=0;$i<$adResult['count'];$i++){
$groupRID = unpack('V',substr($adResult[$i]['objectsid'][0],24));
if($minGroupRID == '' || $minGroupRID>$groupRID[1]){
$minGroupRID = $groupRID[1];
}
}
$sidPrefix = substr($adResult[$i-1]['objectsid'][0],0,24); //Read last objectsid and cut off the prefix
$nextStepGroupRID = $minGroupRID;
do{ //Search for all objects with a lower objectsid than minGroupRID
$adResult = $adConn->search('(&'.$filter.'(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID-$searchStep))).'))', $attributes);
for($i=0;$i<$adResult['count'];$i++){
$RID = unpack('V',substr($adResult[$i]['objectsid'][0],24)); //Extract the relative SID from the SID
$RIDs[] = $RID[1];
$resultSet = array();
foreach($attributes as $attribute){
$resultSet[$attribute] = $adResult[$i][$attribute][0];
}
$result[$RID[1]] = $resultSet;
}
$nextStepGroupRID = $nextStepGroupRID-$searchStep;
}while($adResult['count']>1);
$nextStepGroupRID = $minGroupRID;
do{ //Search for all object with a higher objectsid than minGroupRID
$adResult = $adConn->search('(&'.$filter.'(objectsid>='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID))).')(objectsid<='.preg_replace('/../','\\\\$0',bin2hex($sidPrefix.pack('V',$nextStepGroupRID+$searchStep))).'))', $attributes);
for($i=0;$i<$adResult['count'];$i++){
$RID = unpack('V',substr($adResult[$i]['objectsid'][0],24)); //Extract the relative SID from the SID
$RIDs[] = $RID[1];
$resultSet = array();
foreach($attributes as $attribute){
$resultSet[$attribute] = $adResult[$i][$attribute][0];
}
$result[$RID[1]] = $resultSet;
}
$nextStepGroupRID = $nextStepGroupRID+$searchStep;
}while($adResult['count']>1);
var_dump($result);
$ adConn-&gt;搜索方法如下所示:
function search($filter, $attributes = false, $base_dn = null) {
if(!isset($base_dn)){
$base_dn = $this->baseDN;
}
$entries = false;
if (is_string($filter) && $this->bind) {
if (is_array($attributes)) {
$search = ldap_search($this->resource, $base_dn, $filter, $attributes);
} else {
$search = ldap_search($this->resource, $base_dn, $filter);
}
if ($search !== false) {
$entries = ldap_get_entries($this->resource, $search);
}
}
return $entries;
}
答案 2 :(得分:1)
当最近的SID之间的距离999更多时,可能发生先前的脚本错误。 示例:
S-1-5-21-3188256696-111411151-3922474875- 1158
S-1-5-21-3188256696-111411151-3922474875- 3359
3359-1158&gt; 999
为了避免这种情况,你需要使用刚性结构
示例:
$tt = '1';
do {
...
$nextStepGroupRID = $nextStepGroupRID - $searchStep;
$tt++;
} while ($tt < '30');
在这个例子中,我们被迫检查999 * 30 * 2 = 59940值。
答案 3 :(得分:1)
我能够使用ldap_control_paged_result
来克服大小限制ldap_control_paged_result 用于通过发送分页控件来启用LDAP分页。以下功能在我的情况下完美运行。这适用于(PHP 5&gt; = 5.4.0,PHP 7)
function retrieves_users($conn)
{
$dn = 'ou=,dc=,dc=';
$filter = "(&(objectClass=user)(objectCategory=person)(sn=*))";
$justthese = array();
// enable pagination with a page size of 100.
$pageSize = 100;
$cookie = '';
do {
ldap_control_paged_result($conn, $pageSize, true, $cookie);
$result = ldap_search($conn, $dn, $filter, $justthese);
$entries = ldap_get_entries($conn, $result);
if(!empty($entries)){
for ($i = 0; $i < $entries["count"]; $i++) {
$data['usersLdap'][] = array(
'name' => $entries[$i]["cn"][0],
'username' => $entries[$i]["userprincipalname"][0]
);
}
}
ldap_control_paged_result_response($conn, $result, $cookie);
} while($cookie !== null && $cookie != '');
return $data;
}
如果您现在已成功更新服务器,则上述功能可以获取所有条目。我正在使用此功能来获取AD中的所有用户。