欢迎各位兄弟 发布技术文章
这里的技术是共享的
随着我正在为我们的组织开发的应用程序套件和其他有用工具的扩展,我发现越来越有必要与我们的 Active Directory 集成。
在我们的 AD 大师 Adam Witt 和 Google 教授的帮助下,我终于成功地提取了我需要的信息,而没有破坏任何东西。
我的目标是维护 AD 组的数据库表,以控制对某些应用程序的访问。
如果你坐在那里认为我有点像白痴,因为我没有根据需要拉动 AD 组,我会在这篇博文的末尾解释所有这些......请在指出什么是盲目之前阅读它明显的。
我为这个项目使用了 PHP LDAP 库。这是指向PHP 站点上条目的链接。请注意先决条件。
这是连接到 LDAP 服务器的方法
1个 2个 3个 4个 5个 | $ldap = ldap_connect( '867.530.09.00' , '389' ); //its not a real IP....its a musical easter egg! if (! $ldap ) { die ( 'No LDAP' ); exit ; } |
这是最简单的部分。
为了成功地从您的 Active Directory 中提取信息,您必须了解它的布局方式。
注意:我指定了解它的布局方式,而不是您认为它的布局方式。
(换句话说:从我的经验中学习!)
使用Microsoft AD 浏览器帮助我将所有内容联系在一起,并准确地找出我必须用作过滤器和搜索字符串的内容。
在我接触到执行任务的大块代码之前,我将介绍我必须解决的每个问题。
LDAP 操作错误
经过一番折腾,我发现有两件事导致了这些。
记得设置选项
1个 2个 | ldap_set_option( $ldap ,LDAP_OPT_REFERRALS,0); ldap_set_option( $ldap ,LDAP_OPT_PROTOCOL_VERSION,3); |
如果您的 AD 管理员喜欢安全之类的东西,您将不得不使用 ldap_bind 命令向 AD 服务器注册!
这是告诉 AD 服务器您的 PHP 脚本凭据的代码
1个 | ldap_bind( $ldap , 'xxxxxxx' , 'thisisnotreallyapassword' ); |
所以现在您可以连接到您的 AD 服务器,并且您的 AD 服务器知道您的 PHP 脚本具有哪些权限和特权。
下一步是告诉 PHP 脚本在哪里查找您想要查找的内容。
这是命令:
1个 | $result = ldap_search( $ldap , $base_dn , $filter , $attributes ); |
参数可能有点棘手,这就是理解您的广告布局至关重要的地方。
$ldap 参数是您之前设置的连接。
$base_dn 是 您要搜索的 AD 结构部分顶部的专有名称。
请注意,base_dn 不限于 DN,您可以添加其他属性。
在此示例中,我将要在 STAFF的 OU(组织单位)内、MAIN、SITE、CA 的 DC(域控制器)内进行搜索。
加载 $base_dn 变量将如下所示:
1个 | $base_dn = "OU=STAFF,DC=MAIN,DC=SITE,DC=CA" ; |
我承认我并没有花很多时间来研究过滤器和属性。我见过一些相当复杂的过滤器示例,所以如果您正在寻找类似的东西,您可以随时使用 Google。如果您找到好的、有效的示例,我会邀请您将它们张贴在此处的评论中。
对于我的示例,我们将在作为 OU 的基的任何子项中查找所有“OU”、“CN”和“DC”属性。
1个 2个 3个 4个 5个 6个 7 8个 9 10 11 12 13 14 15 16 17 | $filter = "(OU=*)" ; $attr = array ( "OU" , "CN" , "DC" ); $result = ldap_search( $ldap , $base_dn , $filter , $attr ); $rescount = ldap_count_entries( $ldap , $result ); $data = ldap_get_entries( $ldap , $result ); echo '<pre>' ; foreach ( $data as $row ) { print_r( $row ); } |
这是一个非常基本的例子。我没有任何示例数据可以向您展示,因为这需要向您发送我们的实际 AD 结构(我认为这会违反我的雇佣合同的某些条款),或者需要我去构建一个 AD 服务器。
我将留给您我用来检查和验证我获得的信息的这段代码。
我想确保我只与有用户的组合作,因此,在我的验证阶段,我使用了这段代码:
1个 2个 3个 4个 5个 6个 7 8个 9 10 11 | foreach ( $info as $k => $v ) { //$v['dn'] becomes my base domain as I want to search its children $sres = ldap_search( $ldap , $v [ 'dn' ], "(CN=*)" , $attrib ); if (ldap_count_entries( $ldap , $sres ) > 0 ) { echo $v [ 'dn' ]. ' has ' .ldap_count_entries( $ldap , $sres ). ' users<br>' ; } echo '<hr>' ; } |
希望它有所帮助。如果您有任何问题或改进,请随时发布。
现在……对于那些想不出任何理由的人,为什么我不会在用户连接时从服务器中拉出 AD 组……。
是的......我知道我可以即时拉动 AD 组(我不傻),我想确保在 AD 服务器不可用(或不响应)的情况下我可以维护安全功能足够快),并且,我希望能够审核访问,这意味着我希望在用户登录时记录用户的 AD 组。
只是因为我知道你们中的一些人在想我是多么愚蠢,不只是在某种审计表中记录组,我将解释为什么我这样做。
简短的回答是 数据库规范化。
数据库规范化是指设计数据库以尽可能避免存储冗余数据的做法。这意味着如果您有一条信息(例如地址)将与另一个实体(例如人)相关联,则您记录该信息一次,然后通过连接表将其与父实体相关联.
我知道有些人很难理解这一点,但请相信我,这是真实的事情。
在这种特定情况下,每次连接时记录用户的组(因为每个组都有多个)会在他们每次登录时为每个组、每个用户创建一行。
当我测试这个项目的第一部分时,我连接了两次并创建了 16 行……8 行两次。
想象一下,随着每天有 100 多个用户连接多次,审计表的增长速度会有多快。
因此,当出于历史审计目的跟踪此类数据时,我会保留所有 AD 组的表格。每个组,除了将其名称存储在表中外,还将具有一个唯一标识符字段,用作该表的主键。
使用此解决方案,我将拥有至少两列的 16 行,而不是 16 行的组名。
第 1 列将包含用户表中我的行的唯一标识符,第 2 列将包含组表中包含单个组的行的唯一标识符。
该解决方案占用的空间少得多,并且通过适当的索引,可以使数据库操作更快、更高效。
还有一个额外的好处是其他数据库程序员不会取笑我。