使用 pre-Windows 2000 域名获取 Active Directory 中的用户详细信息

Get user details in Active Directory using pre-Windows 2000 domain name

我不熟悉 LDAP,但我正在将我的应用程序用户与 Active Directory 集成。我想完成两件事:

  1. 验证用户名和密码。
  2. 获取用户详细信息。

不管正确与否,第一部分似乎有效:

$ldap = ldap_connect('ldap:foo.example.local ldap:bar.example.local');
if(!$ldap){
    throw new RuntimeException();
}
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
$is_valid_user = ldap_bind($ldap, 'john.doe@example.local', 'passw0rd');
$is_valid_user = ldap_bind($ldap, 'example\john.doe', 'passw0rd');

不幸的是,如果我使用 Windows 2000 域名格式,第二部分只能检索结果,example.local:

$results = ldap_search($ldap, 'DC=example,DC=local', '(sAMAccountName=john.doe)');
if(!$results){
    return new RuntimeException();
}
ldap_sort($ldap, $results, 'sn');
$info = ldap_get_entries($ldap, $results);
ldap_close($ldap);

使用前Windows 2000 个域名(即仅使用 'DC=example' 作为 ldap_search 的第二个参数)ldap_get_entries() 函数 returns:

array (
    'count' => 0,
)

我知道我们现在是 2019 年,但我希望我的 class 尽可能通用。

我可以做一个简单的更改来使我的代码与两种格式兼容吗?

因此,如果您在 Active Directory 用户和计算机中查看用户的 "Account" 选项卡,"User logon name" 对应于用户的 userPrincipalName attribute. The "User logon name (pre-Windows 2000)" is a combination of the NetBIOS domain name (the "short" domain name) and the sAMAccountName 属性。

其中任何一个都可以用来登录。如您的代码所示,您只需要其中一行,因为它们都做同样的事情。

$is_valid_user = ldap_bind($ldap, 'john.doe@example.local', 'passw0rd');
$is_valid_user = ldap_bind($ldap, 'example\john.doe', 'passw0rd');

在第二个中,您实际上可以删除 example\,只要用户 john.doe 与您连接的域相同。如果用户凭据来自不同的域(如果您在域之间有信任,这是可能的),您只需要提供域名。

指定userPrincipalName时必须包含@example.local,因为那实际上是属性的全部部分,@之后的部分不一定需要匹配域名.

这个:

$results = ldap_search($ldap, 'DC=example,DC=local', '(sAMAccountName=john.doe)');

只有当您 "use the Windows 2000 domain name format" 时才有效,因为这就是您要搜索的全部内容。如果您想匹配任何一种格式,您也需要匹配 userPrincipalName。您可以使用 OR 运算符 |:

$results = ldap_search($ldap, 'DC=example,DC=local', '(|(sAMAccountName=john.doe)(userPrincipalName=john.doe))');

然后您的用户可以给您任何一种格式,您应该能够找到该帐户。

With pre-Windows 2000 domain names (i.e., using just 'DC=example' as second argument for ldap_search)

那将永远行不通,因为 ldap_search 中的第二个参数是 "base DN"。这是您要搜索的容器的 distinguishedName。这通常是域的顶级。顶级域的 distinguishedName 将是 DC=example,DC=local'. But you could also put the DN of an OU there if you only want to look in that OU, like:OU=Users,DC=example,DC=local'

我已经用 编写了一个概念证明(他提供了很好的信息,错误是我的):

<?php

$ldap = ldap_connect('ldap:foo.example.local ldap:bar.example.local');
if(!$ldap){
    throw new RuntimeException();
}
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
if(!ldap_bind($ldap, 'example\lookup.user', 'passw0rd')){
    throw new RuntimeException();
}

$result = ldap_read($ldap, '', '(objectClass=*)', ['defaultNamingContext']);
$info = ldap_get_entries($ldap, $result);
if(isset($info[0]['defaultnamingcontext'][0])){
    $base_dn = $info[0]['defaultnamingcontext'][0]; // E.g. 'DC=example,DC=local'
}else{
    throw new RuntimeException();
}

$results = ldap_search($ldap, $base_dn, '(|(sAMAccountName=john.doe)(userPrincipalName=john.doe))');
if(!$results){
    return new RuntimeException();
}
$info = ldap_get_entries($ldap, $results);
var_dump($info);
ldap_close($ldap);