1 <sect1 id="zend.auth.introduction">
6 Zend_Auth 为认证(authentication)和一些通用用例情景的具体认证适配器提供了一个API。
10 Zend_Auth 只涉及 <emphasis role="strong">认证</emphasis>而不是<emphasis role="strong">授权</emphasis>。认证被宽松地定义为基于一些证书(credential)来确定一个实体(例如,身份)是否确实是它所声称的。授权是一个过程,它决定是否允许一个实体对其他实体进行访问、执行操作,它超出了Zend_Auth的范围。更多关于Zend Framework 授权和访问控制的信息,参见<link linkend="zend.acl">Zend_Acl</link>.
15 <code>Zend_Auth</code> 类通过它的<code>getInstance()</code>方法实现 Singleton 模式 - 只有一个实例可用。这意味着使用 <code>new</code>操作符和 <code>clone</code> 关键字将不能在<code>Zend_Auth</code> 类中工作,而要使用 <code>Zend_Auth::getInstance()</code>来代替。
19 <sect2 id="zend.auth.introduction.adapters">
24 Zend_Auth适配器被用来依靠特定的认证服务(例如LDAP、RDBMS或基于文件的存储)来认证。不同的适配器可能有不同的选项和行为,但有些基本的事情在认证适配器中是通用的。例如,接受认证证书(包括声称身份)、依靠认证服务执行查询、返回结果在Zend_Auth适配器中是通用的。
28 每个Zend_Auth适配器类都实现<code>Zend_Auth_Adapter_Interface</code>。这个接口定义了一个方法<code>authenticate()</code>,适配器必须为执行认证查询而实现它。在调用<code>authenticate()</code>之前,每个适配器必需准备就绪。这样适配器准备包括设置证书(例如,用户名和密码)并为适配器专用的配置选项定义一些值,例如为数据库表适配器做的连接设置。
32 下面是一个认证适配器的例子,它要求为认证设置用户名和密码。为简明扼要,其它的细节(如查询认证服务)被省略了。
33 <programlisting role="php"><![CDATA[
34 class MyAuthAdapter implements Zend_Auth_Adapter_Interface
37 * Sets username and password for authentication
41 public function __construct($username, $password)
47 * Performs an authentication attempt
49 * @throws Zend_Auth_Adapter_Exception If authentication cannot
51 * @return Zend_Auth_Result
53 public function authenticate()
61 如上面所示,<code>authenticate()</code>必需返回一个<code>Zend_Auth_Result</code>的实例(或从<code>Zend_Auth_Result</code>派生的一个类的实例)。如果因为某些原因认证查询不能执行,<code>authenticate()</code>应该抛出一个由<code>Zend_Auth_Adapter_Exception</code>产生的异常。
66 <sect2 id="zend.auth.introduction.results">
71 为了表示一个认证尝试的结果,Zend_Auth适配器返回一个带有<code>authenticate()</code>的<code>Zend_Auth_Result</code>的实例。适配器基于结构组成<code>Zend_Auth_Result</code>对象,下面四个方法提供了一组基本的用户面临的通用Zend_Auth适配器结果的操作:
75 <code>isValid()</code> - 返回 true 当且仅当结果表示一个成功的认证尝试
80 <code>getCode()</code> - 返回一个 <code>Zend_Auth_Result</code> 常量标识符用来决定认证失败的类型或者是否认证成功。这个可以用于开发者希望区别若干认证结果类型的情形,例如,这允许开发者维护详细的认证结果统计。尽管开发这被鼓励去考虑提供这样详细的原因给用户的风险,替而代之使用一般的认证失败信息,这个功能的其它用法是由于可用性的原因提供专用的,定制的信息给用户。更多的信息,参见下面的注释。
85 <code>getIdentity()</code> - 返回认证尝试的身份
90 <code>getMessages()</code> - 返回关于认证尝试失败的数组
96 为了执行更多的操作,开发者可能希望基于认证结果的类型来分支化。一些开发者可能发信有用的操作是:在太多的不成功的密码尝试之后锁住帐号,在太多不存在的身份尝试后标记IP地址,并提供专用的,定制的认证结果信息给用户。下面的结果代码是可用的:
98 <programlisting role="php"><![CDATA[
99 Zend_Auth_Result::SUCCESS
100 Zend_Auth_Result::FAILURE
101 Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND
102 Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS
103 Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID
104 Zend_Auth_Result::FAILURE_UNCATEGORIZED
111 下面的例子举例说明开发者如何分支化结果代码:
112 <programlisting role="php"><![CDATA[
113 // inside of AuthController / loginAction
114 $result = $this->_auth->authenticate($adapter);
116 switch ($result->getCode()) {
118 case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND:
119 /** do stuff for nonexistent identity **/
122 case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
123 /** do stuff for invalid credential **/
126 case Zend_Auth_Result::SUCCESS:
127 /** do stuff for successful authentication **/
131 /** do stuff for other failure **/
141 <sect2 id="zend.auth.introduction.persistence">
143 <title>身份的持久(Persistence)</title>
146 实质上,认证一个包含认证证书的请求很有用,但是维护已认证的身份并在每次请求时不需要出示认证证书也同样很重要。
150 HTTP是一个无连接的协议,然而,象cookie和session这样的技术已经被开发出来使在服务器端的web应用维护多请求状态变得容易。
153 <sect3 id="zend.auth.introduction.persistence.default">
155 <title>在PHP Session 中的缺省持久(Persistence)</title>
158 缺省地,<code>Zend_Auth</code>从使用PHP session成功的认证尝试中提供身份的持久存储。基于一个成功的认证尝试,<code>Zend_Auth::authenticate()</code>通过把认证结果放入持久存储中来保存身份。除非另有配置,<code>Zend_Auth</code> 使用名称为<code>Zend_Auth_Storage_Session</code> 的存储类,这个类使用<link linkend="zend.session">Zend_Session</link>。通过实现<code>Zend_Auth_Storage_Interface</code>给<code>Zend_Auth::setStorage()</code>提供一个对象,一个定制的类可以被替代使用。
163 对于特定的用例,如果身份的持久存储不合适,开发者可以放弃使用<code>Zend_Auth</code>类,替代地,而直接使用适配器类。
167 <example id="zend.auth.introduction.persistence.default.example">
169 <title>修改 Session 名字空间</title>
172 <code>Zend_Auth_Storage_Session</code>使用<code>'Zend_Auth'</code>的seesion名字空间。通过给<code>Zend_Auth_Storage_Session</code>的构造器传递不同的值,这个名字空间可以被替换,并且这个值被从内部传递给<code>Zend_Session_Namespace</code>的构造器。这应该发生在认证尝试之前,因为<code>Zend_Auth::authenticate()</code>执行身份的自动存储。
174 <programlisting role="php"><![CDATA[
175 // Save a reference to the Singleton instance of Zend_Auth
176 $auth = Zend_Auth::getInstance();
178 // Use 'someNamespace' instead of 'Zend_Auth'
179 $auth->setStorage(new Zend_Auth_Storage_Session('someNamespace'));
182 * @todo Set up the auth adapter, $authAdapter
185 // Authenticate, saving the result, and persisting the identity on
187 $result = $auth->authenticate($authAdapter);
197 <sect3 id="zend.auth.introduction.persistence.custom">
199 <title>实现订制存储</title>
202 有时候开发者需要使用不同的身份持久行为,而不是<code>Zend_Auth_Storage_Session</code>提供的。对于这样的案例开发者可以简单地实现<code>Zend_Auth_Storage_Interface</code>并给<code>Zend_Auth::setStorage()</code>提供一个类的实例。
205 <example id="zend.auth.introduction.persistence.custom.example">
207 <title>使用定制存储类</title>
210 为了使用不同于<code>Zend_Auth_Storage_Session</code>的身份之久存储类,开发者可实现<code>Zend_Auth_Storage_Interface</code>:
212 <programlisting role="php"><![CDATA[
213 class MyStorage implements Zend_Auth_Storage_Interface
216 * Returns true if and only if storage is empty
218 * @throws Zend_Auth_Storage_Exception If it is impossible to
219 * determine whether storage
223 public function isEmpty()
226 * @todo implementation
231 * Returns the contents of storage
233 * Behavior is undefined when storage is empty.
235 * @throws Zend_Auth_Storage_Exception If reading contents from
236 * storage is impossible
239 public function read()
242 * @todo implementation
247 * Writes $contents to storage
249 * @param mixed $contents
250 * @throws Zend_Auth_Storage_Exception If writing $contents to
251 * storage is impossible
254 public function write($contents)
257 * @todo implementation
262 * Clears contents from storage
264 * @throws Zend_Auth_Storage_Exception If clearing contents from
265 * storage is impossible
268 public function clear()
271 * @todo implementation
281 为了使用这个定制的存储类,在认证查询被尝试前,<code>Zend_Auth::setStorage()</code>被调用:
282 <programlisting role="php"><![CDATA[
283 // Instruct Zend_Auth to use the custom storage class
284 Zend_Auth::getInstance()->setStorage(new MyStorage());
287 * @todo Set up the auth adapter, $authAdapter
290 // Authenticate, saving the result, and persisting the identity on
292 $result = Zend_Auth::getInstance()->authenticate($authAdapter);
304 <sect2 id="zend.auth.introduction.using">
306 <title>使用Zend_Auth</title>
309 这里提供了两种方法使用Zend_Auth适配器:
313 非直接地,通过<code>Zend_Auth::authenticate()</code>
318 直接地,通过适配器的 <code>authenticate()</code> 方法
325 下面的例子通过<code>Zend_Auth</code>类来示例如何非直接地使用Zend_Auth适配器:
326 <programlisting role="php"><![CDATA[
327 // Get a reference to the singleton instance of Zend_Auth
328 require_once 'Zend/Auth.php';
329 $auth = Zend_Auth::getInstance();
331 // Set up the authentication adapter
332 $authAdapter = new MyAuthAdapter($username, $password);
334 // Attempt authentication, saving the result
335 $result = $auth->authenticate($authAdapter);
337 if (!$result->isValid()) {
338 // Authentication failed; print the reasons why
339 foreach ($result->getMessages() as $message) {
343 // Authentication succeeded; the identity ($username) is stored
345 // $result->getIdentity() === $auth->getIdentity()
346 // $result->getIdentity() === $username
353 一旦在一个请求里的认证被尝试,如上面的例子,检查一个成功的被认证的身份是否存在就是一个简单的匹配:
354 <programlisting role="php"><![CDATA[
355 $auth = Zend_Auth::getInstance();
356 if ($auth->hasIdentity()) {
357 // Identity exists; get it
358 $identity = $auth->getIdentity();
365 从持久存储空间出去一个身份,可简单地使用<code>clearIdentity()</code>方法。这将被典型地用作“logout”操作。
366 <programlisting role="php"><![CDATA[
367 Zend_Auth::getInstance()->clearIdentity();
373 当自动使用持久存储空间对特定的用例不合适,开发者可简单地忽略<code>Zend_Auth</code>类,直接使用适配器类。直接使用适配器类需要配置和准备适配器对象和调用它的<code>authenticate()</code>方法。适配器规范细节将在每个适配器的文档中讨论。下面的例子直接使用 <code>MyAuthAdapter</code>:
374 <programlisting role="php"><![CDATA[
375 // Set up the authentication adapter
376 $authAdapter = new MyAuthAdapter($username, $password);
378 // Attempt authentication, saving the result
379 $result = $authAdapter->authenticate();
381 if (!$result->isValid()) {
382 // Authentication failed; print the reasons why
383 foreach ($result->getMessages() as $message) {
387 // Authentication succeeded
388 // $result->getIdentity() === $username