Zend_Auth_Adapter_Ldap
用 LDAP 服务支持 web 程序认证。它的功能包括用户名和域名规范化、多域认证和实效切(failover)换能力。经测试,它能与 Microsoft Active Directory 和 OpenLDAP 一起工作,它也应该能和其它 LDAP 服务提供者一起工作。
本文档包括使用 Zend_Auth_Adapter_Ldap
的指南、它的 API 、各种可用选项的大纲、认证问题故障排除的诊断信息和Active Directory 与 OpenLDAP 服务器的范例选项。
为了快速把 Zend_Auth_Adapter_Ldap
认证集成到你的程序中,即使你不使用 Zend_Controller
,代码的基本部分看起来是这样的:
$username = $this->_request->getParam('username'); $password = $this->_request->getParam('password'); $auth = Zend_Auth::getInstance(); $config = new Zend_Config_Ini('../application/config/config.ini', 'production'); $log_path = $config->ldap->log_path; $options = $config->ldap->toArray(); unset($options['log_path']); $adapter = new Zend_Auth_Adapter_Ldap($options, $username, $password); $result = $auth->authenticate($adapter); if ($log_path) { $messages = $result->getMessages(); $logger = new Zend_Log(); $logger->addWriter(new Zend_Log_Writer_Stream($log_path)); $filter = new Zend_Log_Filter_Priority(Zend_Log::DEBUG); $logger->addFilter($filter); foreach ($messages as $i => $message) { if ($i-- > 1) { // $messages[2] and up are log messages $message = str_replace("\n", "\n ", $message); $logger->log("Ldap: $i: $message", Zend_Log::DEBUG); } } }
虽然日志( logging )代码是可选的,还是强烈建议使用日志。Zend_Auth_Adapter_Ldap
将记录任何想要的信息细节到 $messages
(更多的信息看下面),对于难以调试的程序来说,有历史记录这是个很好的功能。
上面用到 Zend_Config_Ini
的代码是来加载适配器选项,它也是可选的,使用一个规则的数组来完成工作。下面是带有两个单独的服务器选项的 application/config/config.ini
文件的例子。带有多组服务器选项的适配器将按顺序来尝试每个服务器直到资格被成功认证。服务器的名字 (例如 server1
and server2
)是任意的,关于选项数组的细节,参见下面 Server Options 一节。 注意 Zend_Config_Ini
要求任何带有等号(=
)的值需要括起来(如下面的 DNs)。
[production] ldap.log_path = /tmp/ldap.log ; Typical options for OpenLDAP ldap.server1.host = s0.foo.net ldap.server1.accountDomainName = foo.net ldap.server1.accountDomainNameShort = FOO ldap.server1.accountCanonicalForm = 3 ldap.server1.username = "CN=user1,DC=foo,DC=net" ldap.server1.password = pass1 ldap.server1.baseDn = "OU=Sales,DC=foo,DC=net" ldap.server1.bindRequiresDn = true ; Typical options for Active Directory ldap.server2.host = dc1.w.net ldap.server2.useSsl = true ldap.server2.accountDomainName = w.net ldap.server2.accountDomainNameShort = W ldap.server2.accountCanonicalForm = 3 ldap.server2.baseDn = "CN=Users,DC=w,DC=net"
上述的配置将指示 Zend_Auth_Adapter_Ldap
首先来尝试用 OpenLDAP 服务器 s0.foo.net
来认证用户,如果不论什么原因认证失败,将尝试 AD 服务器 dc1.w.net
。
这个配置示例了在不同的域的服务器的多域认证,也可以在同一域中用多个服务器来提供冗余。
注意在这个例子中,即使 OpenLDAP 不需要用于 Windows 的短 NetBIOS 风格的域名,我们仍在这里提供它以保证命名正规化 (参见下面的 Username Canonicalization 一节)。
Zend_Auth_Adapter_Ldap
构造器接受三个参数。
$options
参数是必需的并且是一个包含一组或多组选项的数组。注意它是 Zend_Ldap 选项的 数组的数组 。即使你只使用一个 LDAP 服务器,选项仍要包含在另一个数组中。
下面是一个选项参数包含两组服务器选项( s0.foo.net
和 dc1.w.net
(和上面 INI 表示法一样的选项))的例子的 print_r()
输出:
Array ( [server2] => Array ( [host] => dc1.w.net [useSsl] => 1 [accountDomainName] => w.net [accountDomainNameShort] => W [accountCanonicalForm] => 3 [baseDn] => CN=Users,DC=w,DC=net ) [server1] => Array ( [host] => s0.foo.net [accountDomainName] => foo.net [accountDomainNameShort] => FOO [accountCanonicalForm] => 3 [username] => CN=user1,DC=foo,DC=net [password] => pass1 [baseDn] => OU=Sales,DC=foo,DC=net [bindRequiresDn] => 1 ) )
上述每组选项提供的信息是不同的,主要是因为当绑定(参见下面 服务器选项 一节中的 bindRequiresDn
选项)时 AD 不要求在 DN 表单中的有用户名,这意味着我们可以忽略很多和为认证用户名获取 DN 相关的选项。
什么是 DN? | |
---|---|
DN 或者 "distinguished name" 是一个字符串,表示在 LDAP 目录中到一个对象的路径。每个用逗号分隔的组件是一个属性并且它的值表示一个节点。组件是按反顺序来算的,例如:用户账户 CN=Bob Carter,CN=Users,DC=w,DC=net 直接位于 CN=Users,DC=w,DC=net container 里的。这种结构用 LDAP 浏览器如 ADSI Edit MMC snap-in for Active Directory 或 phpLDAPadmin 可以最好地浏览。 |
服务器名(如上面的 'server1
' 和 'server2
')是任意的,但因为使用 Zend_Config
,标识符(identifiers)应当以数字索引的相反出现并且不应当包含任何用于相关文件格式(例如,'.
' INI 属性分隔符,XML 条目参考 '&
' 等)的特殊字符。
用多组服务器选项,适配器可以在多域的环境中认证用户并提供 failover (估计是失败后尝试下一个服务器),所以如果一个服务器不可用,将查询另一个。
非常详细的介绍 (The Gory Details)- 在认证方法中到底发生了什么? | |
---|---|
当调用 |
Zend_Auth_Adapter_Ldap
构造器的用户名和密码参数是要被认证的证书(例如,用户通过 HTML 登录表单提供的证书(credentials))。另外,也可以通过 setUsername()
和 setPassword()
方法来设置。
在 Zend_Auth_Adapter_Ldap 的上下文中 的每组服务器选项包含下列选项,它们基本上不可修改地传递给 Zend_Ldap::setOptions()
:
表 4.2. 服务器选项
名称 | 描述 |
---|---|
host | 这些选项表示的 LDAP 服务器的主机名,该选项是必需的。 |
port |
LDAP 服务器监听的端口,如果 useSsl 是 true ,缺省 端口 值是 636。如果 useSsl 是 false ,缺省 端口 值是 389。
|
useSsl |
如果是 true ,表示 LDAP 客户端应当使用 SSL / TLS 加密传输。在生产环境中强烈建议使用 true 值以防止明文传输密码。缺省值为 false 是因为服务器经常在安装之后请求被分别安装的证书。它也改变缺省 端口 值( 见上面 端口 的描述)。
|
username |
账户的 DN, 用来执行账户 DN 查找。LDAP servers that require the username to be in DN form when performing the "bind" require this option (这句没有理解)。
如果 bindRequiresDn 是 true ,这个选项是必需的。这个账户不需要是优先账户 - a account with read-only
access to objects under the baseDn is all that is necessary (and preferred based on the Principle of Least Privilege).
|
password | 账户的密码,用来执行账户 DN 查找。如果没有提供这个选项,当执行账户 DN 查找时,LDAP 客户端将尝试“匿名绑定”。 |
bindRequiresDn |
一些 LDAP 服务器要求用户名以 DN 格式来绑定,如 CN=Alice Baker,OU=Sales,DC=foo,DC=net (基本上 除了 AD 以外所有的服务器)。如果这个选项是 true ,Zend_Ldap 自动获取被认证的用户所对应的 DN。如果它不是 DN 格式,那就重新绑定合适的 DN。缺省值是 false 。目前,当绑定时,只有微软的 Active Directory 服务器(ADS) 不 要求用户名为 DN 格式,所以和 AD 一起使用,这个选项可以是 false (而且应当是,因为获取 DN 需要额外的过程(round trip)到服务器),否则,这个选项必需设置为 true (例如,OpenLDAP)。当搜索账户时,这个选项也控制缺省的 acountFilterFormat,参见 accountFilterFormat 选项。
|
baseDn | 定位所有被认证账户下的 DN,这个选项是必需的。如果你不能确定正确的 baseDn 值,可以用 DC= 组件从用户的 DNS 域来产生它,例如,如果用户的基本名是 [email protected],DC=foo,DC=net 的 baseDn 应当工作。然而更精确的位置(例如 OU=Sales,DC=foo,DC=net )将更有效。 |
accountCanonicalForm |
一个是 2、3 或 4 的值,用来指示那个账户名在成功认证后需要规范化。值的解释具体如下:2 表示传统的用户名(例如 alice ),3 表示反斜杠式(backslash-style)名称(例如 FOO\alice),或者 4 表示基本式用户名(例如 [email protected])。缺省值为 4 (例如 [email protected] )。例如,当值为 3,由 Zend_Auth_Result::getIdentity() (如果使用了 Zend_Auth ,则是 Zend_Auth::getIdentity() ,) 返回的身份(identity)将总是 FOO\alice,不论 Alice 提供了什么格式,如 alice、 [email protected]、 FOO\alice、FoO\aLicE、 foo.net\alice 等。见 Zend_Ldap 中的 Account Name Canonicalization 一节有更多的细节。注意当使用多组服务器选项时,建议但不要求所有服务器选项使用相同的 accountCanonicalForm,这样,用户名对于同一格式总是规范化的(例如,对于 AD 服务器规范化为 EXAMPLE\username,但对于 OpenLDAP 服务器规范化为 [email protected],对于程序的高水平(high-level)逻辑,这可能很不好用。)
|
accountDomainName |
目标 LDAP 服务器的 FQDN 域名是一个授权(authority)(例如 example.com )。该选项用来规范化名字,这样用户提供的用户名可以为绑定按需转换。它也可用来决定是否服务器对用户名是一个授权(例如 accountDomainName 是 foo.net 并且用户提供了 [email protected],将不查询服务器并导致一个错误)。该选项不是必需的,但如果不提供,那就不支持用户名为基本名(principal name)格式(例如 [email protected])。强烈建议使用该选项,因为许多用例要求生成基本名格式。
|
accountDomainNameShort | 目标 LDAP 服务器的 ‘短’ 域名是一个授权(authority)(例如 FOO)。注意按 1:1 来映射 accountDomainName 和 accountDomainNameShort。该选项用于为 Windows 网络指定 NetBIOS 名但也可用于非 AD 服务器(例如,当多组服务器选用使用反斜杠风格的 accountCanonicalForm时为了保持一致性)。该选项不是必需的但如果不使用,就不支持反斜杠格式的用户名(例如 FOO\alice)。 |
accountFilterFormat |
用来搜索账户的 LDAP 搜索过滤器。这个字符串是个 printf() 风格的表达式,必需包含一个 '%s ' 来适合用户名。缺省值为 '(&(objectClass=user)(sAMAccountName=%s)) ',除非 bindRequiresDn 设置为 true ,那样缺省值就是 '(&(objectClass=posixAccount)(uid=%s)) '。例如,如果因为某种原因你想对 AD 使用 bindRequiresDn = true ,需要设置 accountFilterFormat = '(&(objectClass=user)(sAMAccountName=%s)) '。
|
注意 | |
---|---|
如果你设置 |
Zend_Auth_Adapter_Ldap
在它的 authenticate()
方法里收集调试信息。这个信息存储在 Zend_Auth_Result
对象里。下面描述由 Zend_Auth_Result::getMessages()
返回的数组:
表 4.3. 调试信息 (Messages)
信息(Messages) 数组索引 | 描述 |
---|---|
Index 0 | 显示给用户的用户友好的一般信息(例如无效的证书(credentials))。如果认证成功,这个字符串是空的。 |
Index 1 | 更详细的错误信息,不适合显示给用户但作为服务器操错日志。如果认证成功,这个字符串是空的。 |
Indexes 2 and higher | 所有日志信息按顺序从 index 2 开始。 |
实践上,index 0 显示给用户(例如使用 FlashMessenger 助手), index 1 作为日志,如果收集到调试信息, index 2 和它以后的 index 也作为日志(尽管最终的信息总是从 index 1 的字符串开始)。
对于 ADS,下列选项值得注意:
表 4.4. Active Directory 的选项
名字 | 另外的注释 |
---|---|
host | 适用所有的服务器,该选项必需。 |
useSsl |
因为安全的缘故,如果服务器安装了必要的证书,这个应该是 true 。
|
baseDn | 适用所有的服务器,该选项必需。缺省地 AD 把所有用户账户放在 Users 容器中 (例如 CN=Users,DC=foo,DC=net),但在大型组织里缺省不常见,要询问 AD 管理员你的程序账户的最好的 DN 是什么。 |
accountCanonicalForm | 几乎可以确定你想要这个值为 3 来使用反斜杠式的名称(例如 FOO\alice),这对于 Windows 用户来说是最熟悉的。你 不 应该使用不合格的格式 2 (例如 alice),因为它可能授权在其它信任域里(例如 BAR\alice 和 FOO\alice 将被当作相同的用户)相同名字的用户访问你的程序。(参见下面的注释) |
accountDomainName | 使用 AD 时这是必需的除非使用 accountCanonicalForm 2 ,再强调一下,我们不鼓励这样用。 |
accountDomainNameShort | AD 服务器是授权的域用户的 NetBIOS 名称。如果使用反斜杠风格 accountCanonicalForm,这个是必需的。 |
注意 | |
---|---|
从技术角度讲,用当前的 |
对于 OpenLDAP 或一般的使用典型的 posixAccount 风格的 LDAP 服务器,下面的选项值得注意:
表 4.5. OpenLDAP 的选项
名字 | 另外的注释 |
---|---|
host | 适用所有的服务器,该选项必需。 |
useSsl |
因为安全的缘故,如果服务器安装了必要的证书,这个应该是 true 。
|
username | 必需并一定是一个 DN,因为当执行绑定时 OpenLDAP 要求 DN 格式的用户名。设法使用无特权的账户。 |
password | 对应上述用户名的密码,如果 LDAP 服务器支持匿名绑定,这个也许会忽略。 |
bindRequiresDn |
必需并一定是 true ,因为当执行绑定时 OpenLDAP 要求 DN 格式的用户名。
|
baseDn | 适用所有的服务器,该选项是必需的并指示所有被认证的账户的 DN 的定位。 |
accountCanonicalForm | 可选但缺省值是 4 (基本风格名如 [email protected]),如果适用反斜杠式的名字(如 FOO\alice)这个也许不是理想的。对于反斜杠式名字值为 3。 |
accountDomainName | 必需,除非使用不推荐的 accountCanonicalForm 2, |
accountDomainNameShort | 如果不使用 AD ,这个值不是必需的。否则,如果使用 accountCanonicalForm 3 ,该选项必需并是个完全对应 accountDomainName 的短名 (例如如果 accountDomainName 是 foo.net,一个好的 accountDomainNameShort 值可能是 FOO)。 |