我目前正在开发一个 Laravel 10 应用程序,该应用程序使用 AdLdap2 包和 PHP 的 LDAP 函数 (PHP 8) 与 Active Directory (AD) 集成。然而,当我尝试从 LDAP 服务器获取所有用户(超过 20,000 个)时,我遇到了挑战。
以下是我尝试过的两种方法: 方法一:使用AdLdap2包
private function handleAdLdapUsersEx($provider): void
{
try {
$pageSize = 200;
if ($this->searchBase->filter) {
$provider->where($this->searchBase->filter);
}
$pagedUsers = $provider->select('distinguishedname', 'givenname', 'name', 'objectguid', 'samaccountname', 'userprincipalname', 'mail')
->whereEnabled()
->limit($pageSize)
->paginate($pageSize, $this->currentPage);
dump($pagedUsers->count());
if ($pagedUsers->count() > 0) {
collect($pagedUsers->getResults())->chunk(100)->each(function ($chunkedUsers) {
$this->handleBulk($chunkedUsers);
unset($chunkedUsers);
gc_collect_cycles();
});
if ($pagedUsers->count() === $pageSize) {
ImportUsersJob::dispatch($this->searchBase, $this->ldapConnector, $this->ldapConfig, $this->currentPage + 1);
}
}
} catch (\Exception $e) {
dd($e->getMessage());
}
}
在这种方法中,我设置了分页限制,即使有限制和分页,我也能在一页中获取所有记录,但仍然遇到超时。设置 public $timeout = 600;有效,但我想避免硬编码超时。
方法2:使用PHP LDAP函数
private function handleAdLdapUsers($provider, $ldapConnector): void
{
try {
$usersFetched = 0;
$lastUserEntry = null;
$ldapConnection = ldap_connect($provider->getConnection()->getHost());
ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_bind($ldapConnection, $ldapConnector->getUsernames(), $ldapConnector->getPassword());
$filter = !empty($this->searchBase->filter) ? $this->searchBase->filter : "(objectClass=*)";
$result = @ldap_search($ldapConnection, $provider->getDn(), $filter, [], 0, 1);
$firstEntry = ldap_first_entry($ldapConnection, $result);
while ($firstEntry) {
$attributes = ldap_get_attributes($ldapConnection, $firstEntry);
$users = $provider->select('distinguishedname', 'givenname', 'name', 'objectguid', 'samaccountname', 'userprincipalname', 'mail', 'usncreated')
->whereEnabled()
->get($attributes);
if ($users->count() > 0) {
$this->handleBulk($users);
$usersFetched = $users->count();
} else {
break;
}
$lastUserEntry = ldap_next_entry($ldapConnection, $firstEntry);
$firstEntry = $lastUserEntry;
}
ldap_unbind($ldapConnection);
} catch (\Exception $e) {
dd($e->getMessage());
}
}
此方法返回“超出大小限制”警告,并且仅获取 1000 条记录。我用 @ldap_search 抑制了警告,但我仍然需要一种方法来获取所有用户(可能超过 50,000 个),而不会达到大小限制或遇到内存问题。
任何帮助或指导将不胜感激!
不要抑制警告。使用
@
抑制它对于使代码获取更多结果没有任何帮助 – 它仍然获得不完整的数据,除了现在它安静地 获得不完整的数据 – 一旦代码被修复,警告将不会出现无论如何。
ldap_search()
采用 $controls
参数,您可以通过该参数请求分页搜索结果。请参阅LDAP 控件。由 ldap_parse_result()
解析的结果将有一个带有“cookie”的相应控件,需要将其传递给另一个 ldap_search() 以检索下一页。
$req_controls = [
["oid" => LDAP_CONTROL_PAGEDRESULTS, "value" => ["size" => 50"]],
];
如果您特别想要用户,请勿过滤
(objectClass=*)
;过滤 (objectCategory=user)
(这是 AD 特定的属性)以减少无用结果的数量。
ldap_search() 还采用
$attributes
参数。现在,您正在检索所有属性,然后仅选择您想要的几个属性,这就像当您想要的只是两列时执行 SELECT * FROM
。不要这样做,而是将所需属性的列表传递给 ldap_search() 以减少传输的数据量(即使不一定是条目量)。