Php 使用Windows 2000以前版本的域名在Active Directory中获取用户详细信息

Php 使用Windows 2000以前版本的域名在Active Directory中获取用户详细信息,php,active-directory,ldap,Php,Active Directory,Ldap,我不熟悉LDAP,但我正在将我的应用程序用户与Active Directory集成。我想完成两件事: 验证用户名和密码 获取用户详细信息 不管正确与否,第一部分似乎有效: $ldap = ldap_connect('ldap:foo.example.local ldap:bar.example.local'); if(!$ldap){ throw new RuntimeException(); } ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERS

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

  • 验证用户名和密码
  • 获取用户详细信息
  • 不管正确与否,第一部分似乎有效:

    $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\u search
    的第二个参数),
    ldap\u get\u entries()函数返回:

    array (
        'count' => 0,
    )
    
    我知道我们是在2019年,但我想让我的类尽可能通用


    是否有一个简单的更改可以使我的代码与这两种格式兼容?

    因此,如果您在Active Directory用户和计算机中查看用户的“帐户”选项卡,“用户登录名”对应于该属性。“用户登录名(Windows 2000之前)”是NetBIOS域名(“短”域名)和用户属性的组合

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

    $is\u valid\u user=ldap\u bind($ldap,'john。doe@example.local","passw0rd",;
    $is_valid_user=ldap_bind($ldap,'example\\john.doe','passw0rd');
    
    在第二个示例中,只要用户
    john.doe
    与您连接的域相同,您就可以实际删除
    example\
    。仅当用户凭据来自不同的域时,才需要提供域名(如果域之间存在信任,则可以这样做)

    在指定
    userPrincipalName
    时,必须包含
    @示例.local
    ,因为这实际上是属性的全部部分,
    @
    后面的部分不一定需要与域名匹配

    这:

    $results=ldap_search($ldap,'DC=example,DC=local','(sAMAccountName=john.doe)');
    
    只有在“使用Windows 2000域名格式”时才有效,因为这就是您要搜索的全部内容。如果要匹配任何一种格式,还需要匹配
    userPrincipalName
    。您可以使用OR运算符
    |

    $results=ldap_search($ldap,'DC=example,DC=local','(|(sAMAccountName=john.doe)(userPrincipalName=john.doe)));
    
    然后你的用户可以给你任何一种格式,你应该能够找到帐户

    使用Windows 2000之前的域名(即,仅使用
    'DC=example'
    作为
    ldap\u搜索的第二个参数)


    这永远不会起作用,因为
    ldap\u search
    中的第二个参数是“基本DN”。这是要搜索的容器的
    区分名称。这通常是域的顶级。域顶级的
    discriminatedname
    将是
    DC=example,DC=local'。但是,如果您只想查看某个OU,也可以将该OU的DN放在那里,例如:
    OU=Users,DC=example,DC=local'

    我已经编写了一个概念证明(他的信息很棒,我的错误):


    如果我问了一些愚蠢的问题,很抱歉,但这是我的LDAP速成课程。您的意思是我绝对需要知道完整的Windows 2000域名(
    示例.local
    )才能搜索用户,因为NetBIOS名称(
    示例
    )不包含足够的信息来标识基本DN?大多数支持LDAP的应用程序都要求基本DN,因为这允许您将范围限制为OU,你可能会想要的。但是,您可以通过查询的
    defaultNamingContext
    属性找到域的基本DN。这里有一个如何在PHP中实现这一点的示例。该示例读取
    supportedcontrol
    属性,因此只需将其替换为
    defaultNamingContext
    。我认为
    (|(sAMAccountName=john.doe)(userPrincipalName=john.doe))中存在错误。
    。至少对于我的Windows域控制器,字段
    userPrincipalName
    的值为
    john。doe@example.com
    ,而不是
    john.doe
    @DanielMarschall在那个特定的案例中你是对的。但这是指使用用户的输入并对
    sAMAccountName
    userPrincipalName
    进行匹配。因此,如果用户给你
    john.doe
    作为他们的用户名,那么是的,搜索查询将与之完全相同,并且它将与
    sAMAccountName
    匹配。但是如果用户提供了
    john。doe@example.com
    ,则查询将是
    (|)(sAMAccountName=john)。doe@example.com)(userPrincipalName=john。doe@example.com))
    并且它最终会与
    userPrincipalName
    匹配。因此在实际使用中,
    john.doe
    不会被硬编码,我认为
    (|(sAMAccountName=john.doe)(userPrincipalName=john.doe))中有一个错误。至少对于我的Windows域控制器,字段
    userPrincipalName
    的值为
    john。doe@example.com
    ,而不是
    john.doe
    @DanielMarschall当用户没有提供完全限定的用户名时,可以使用一个默认的(或通常只有一个)域。但这只是一个概念的快速证明,我并没有真正的多域设置来测试。
    <?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);