更新:本文现已提供法语版本,由FrédéricBlanc提供。
我已经回答了一些想知道如何在ZendFramework中处理身份验证和身份持久性的人提出的问题。典型的问题是他们不确定如何组合:
- 身份验证适配器
- 登录表单
- 用于登录/注销操作的控制器
- 在后续请求中检查经过身份验证的用户
这并不难,但确实需要了解MVC的各个部分如何组合在一起,以及如何使用Zend_Auth
。让我们来看看。
身份验证适配器
要使所有这些工作正常,您需要一个身份验证适配器。我不打算对此进行详细说明,因为文档涵盖了它们,并且您的需求会因您的站点而异。但是,我将假设您的身份验证适配器需要用户名和密码来进行身份验证。
我们的登录控制器将使用适配器,但只是有一个用于检索它的占位符。
登录表单
登录表单本身非常简单。您可以设置一些基本的验证规则,以防止数据库或其他服务命中,但要保持相对简单。出于本教程的目的,我们将定义以下标准:
- 用户名只能是字母字符,并且必须包含3到20个字符
- 密码必须只能是字母数字字符,并且必须是6到20个字符
表单看起来像这样:
class LoginForm extends Zend_Form { public function init() { $username = $this->addElement('text', 'username', array( 'filters' => array('StringTrim', 'StringToLower'), 'validators' => array( 'Alpha', array('StringLength', false, array(3, 20)), ), 'required' => true, 'label' => 'Your username:', )); $password = $this->addElement('password', 'password', array( 'filters' => array('StringTrim'), 'validators' => array( 'Alnum', array('StringLength', false, array(6, 20)), ), 'required' => true, 'label' => 'Password:', )); $login = $this->addElement('submit', 'login', array( 'required' => false, 'ignore' => true, 'label' => 'Login', )); // We want to display a 'failed authentication' message if necessary; // we'll do that with the form 'description', so we need to add that // decorator. $this->setDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')), array('Description', array('placement' => 'prepend')), 'Form' )); } }
登录控制器
现在,让我们创建一个控制器来处理登录和注销操作。典型的流程是:
- 用户点击登录表单
- 用户提交表单
- 控制器处理表单
- 验证错误重新显示带有错误消息的表单
- 成功验证重定向到主页
- 登录用户被重定向到主页
- 注销操作注销用户并重定向到登录表单
李>
LoginController
将使用您选择的身份验证适配器以及登录表单。我们将把表单和方法传递给登录表单构造函数(因为我们现在知道它们对于表单的这种用法是什么)。当我们有有效值时,我们会将它们传递给我们的身份验证适配器。
那么,让我们来创建控制器。首先,我们将为表单和身份验证适配器创建访问器。
class LoginController extends Zend_Controller_Action { public function getForm() { return new LoginForm(array( 'action' => '/login/process', 'method' => 'post', )); } public function getAuthAdapter(array $params) { // Leaving this to the developer... // Makes the assumption that the constructor takes an array of // parameters which it then uses as credentials to verify identity. // Our form, of course, will just pass the parameters 'username' // and 'password'. } }
接下来,我们需要在派发任何操作之前进行一些检查,以确保:
- 如果用户已经通过认证,但是没有请求退出,我们应该重定向到首页
- 如果用户没有通过认证,但是已经请求退出,我们应该重定向到登录页面
下面的preDispatch()
例程将为我们做这件事:
class LoginController extends Zend_Controller_Action { // ... public function preDispatch() { if (Zend_Auth::getInstance()->hasIdentity()) { // If the user is logged in, we don't want to show the login form; // however, the logout action should still be available if ('logout' != $this->getRequest()->getActionName()) { $this->_helper->redirector('index', 'index'); } } else { // If they aren't, they can't logout, so that action should // redirect to the login form if ('logout' == $this->getRequest()->getActionName()) { $this->_helper->redirector('index'); } } } }
现在,我们需要做我们的登录表单。这是我们最简单的方法——我们只需检索表单并将其分配给视图:
class LoginController extends Zend_Controller_Action { // ... public function indexAction() { $this->view->form = $this->getForm(); } }
处理表单涉及的逻辑稍微多一些。我们需要验证我们有一个发布请求,然后验证表单是否有效,最后验证凭据是否有效。
class LoginController extends Zend_Controller_Action { // ... public function processAction() { $request = $this->getRequest(); // Check if we have a POST request if (!$request->isPost()) { return $this->_helper->redirector('index'); } // Get our form and validate it $form = $this->getForm(); if (!$form->isValid($request->getPost())) { // Invalid entries $this->view->form = $form; return $this->render('index'); // re-render the login form } // Get our authentication adapter and check credentials $adapter = $this->getAuthAdapter($form->getValues()); $auth = Zend_Auth::getInstance(); $result = $auth->authenticate($adapter); if (!$result->isValid()) { // Invalid credentials $form->setDescription('Invalid credentials provided'); $this->view->form = $form; return $this->render('index'); // re-render the login form } // We're authenticated! Redirect to the home page $this->_helper->redirector('index', 'index'); } }
最后,我们可以处理注销操作。这几乎和显示登录表单一样简单;我们只需从身份验证对象中清除身份,然后重定向:
class LoginController extends Zend_Controller_Action { // ... public function logoutAction() { Zend_Auth::getInstance()->clearIdentity(); $this->_helper->redirector('index'); // back to login page } }
好的,这就是我们的登录/注销例程。让我们看一下我们拥有的一个关联视图,形式为:
<? // login/index.phtml ?> <h2>Please Login</h2> <?= $this->form ?>
就是这样。真的。Zend_Form
使视图脚本变得简单。:-)
检查经过身份验证的用户
问题区的最后一部分是:如何判断用户是否已通过身份验证,如果没有则限制访问?
如果您仔细查看上面的preDispatch()
方法,您就会明白这是如何完成的。Zend_Auth
在会话中保留身份,允许您使用此构造直接查询它:
Zend_Auth::getInstance()->hasIdentity()
你可以用它来判断用户是否登录,如果没有登录则使用redirector重定向到登录页面。您也可以从auth对象中提取身份:
$identity = Zend_Auth::getInstance()->getIdentity();
这可以添加到帮助程序中以在您的布局中显示登录状态,例如:
/** * ProfileLink helper * * Call as $this->profileLink() in your layout script */ class My_View_Helper_ProfileLink { public $view; public function setView(Zend_View_Interface $view) { $this->view = $view; } public function profileLink() { $auth = Zend_Auth::getInstance(); if ($auth->hasIdentity()) { $username = $auth->getIdentity()->username; return '<a href=\"/profile' . $username . '\">Welcome, ' . $username . '</a>'; } return '<a href=\"/login\">Login</a>'; } }
结论
Zend_Auth
做了很多幕后工作,使在会话中持久化身份变得微不足道。将它与Zend_Form
相结合,您就拥有了一个非常易于实施的检索和验证凭据的解决方案;在Zend_Controller
组件中添加标准挂钩以在发送之前过滤操作,并且您可以根据身份验证状态轻松限制对应用程序的访问。