В статье приведен обзор возможностей компоненты
Автор: Роб Ален, http://akrabat.com
Оригинал: http://akrabat.com/zend-auth-tutorial
Перевод: Александр Мусаев, http://paradigm.ru
PDF-версия для печати: http://archive.paradigm.ru/zend_auth.pdf
В контексте веб-приложений, под аутентификацией обычно подразумевается проверка соответствия пользователя его учетной записи на веб-сервере с помощью имени («логина») и пароля. В качестве примера реализации подобного механизма на базе Zend Framework, мы дополним подобной проверкой базу компакт-дисков (веб-приложение, реализованное в статье «Введение в Zend Framework»).
Для этого нам понадобится:
При использовании MySQL, такую таблицу можно будет создать следующим запросом:
Нам понадобится добавить в нее учетную запись тестового пользователя:
Запустите данные SQL-запросы с помощью любой клиентской программы для MySQL. Имя пользователя и пароль при желании можно сменить на любые другие значения.
Нам понадобится внести следующие изменения в файл начальной загрузки:
Все, что необходимо здесь выполнить, — убедиться в том, что класс
В конструкторе класса-контроллера инициализируется вид и присваивается значение переменной
Форма входа в систему достаточно проста и содержит всего два поля — для имени пользователя и его пароля.
Шаблон отображает
Теперь, когда наша форма готова, можем перейти к созданию контроллера для работы с ней.
Для отображения формы нужно задать ее заголовок и текст сообщения, после чего форму можно будет увидеть, перейдя по URL
Рассмотрим приведенный выше код пошагово:
Здесь, как обычно, мы извлекаем имя пользователя и пароль из массива POST и обрабатываем их значения HTML-фильтром. Использованная при этом функция
Процесс аутентификации продолжается, только если имя пользователя задано. В случае пустого значения, попытка выполнить аутентификацию через
Для работы с авторизационными данными в
Так же мы должны передать в адаптер точные значения имени пользователя и пароля, которые были введены в форму.
Для того, чтобы выполнить саму процедуру аутентификации, выполняется вызов метода
В случае, если аутентификация прошла успешно, учетная запись пользователя из базы данных будет полностью сохранена внутри синглетона
В том случае, если проверка имени пользователя и пароля не прошла, мы сообщить сообщаем об этом пользователю через переменную
Функция
Нам понадобится предоставить пользователю специальную ссылку, перейдя по которой он смог бы выходить из веб-приложения. Проще всего сделать ее внутри шаблона
Очень удобно, что
Теперь нам понадобится внести изменения в файл
Приведенный код не содержит ничего принципиально нового. Мы используем
Функции выхода из системы готова.
Функция со стандартным именем
На этом работа над авторизацией завершена.
Все замечания относительно оригинальной статьи вы можете отправлять ее автору по адресу rob@akrabat.com. Комментарии к русскому переводу шлите на musayev@yandex.ru.
Update: архив с примерами программ из статьи можно найти на сайте автора: zend_auth-tutorial_104.zip.
Zend_Auth
, дающий общее представление о реализации пользовательской авторизации в приложениях на базе Zend Framework. В качестве основы приводимых примеров, использованы материалы статьи «Введение в Zend Framework». Примеры протестированы на Zend Framework версий 0.9, 0.9.1 и 0.9.2, и скорее всего будут работать с более поздними версиями, но не с более ранними. Автор: Роб Ален, http://akrabat.com
Оригинал: http://akrabat.com/zend-auth-tutorial
Перевод: Александр Мусаев, http://paradigm.ru
PDF-версия для печати: http://archive.paradigm.ru/zend_auth.pdf
Перед тем как начать
Реализация механизма аутентификации пользователей, который мы будем использовать в наших примерах, основана на PHP-сессиях. Убедитесь, что в качестве параметра session.save_path в вашемphp.ini
задана директория, доступная веб-серверу для записи.Аутентификация
Аутентификацией или подтверждением подлинности называется процедура проверки соответствия субъекта и того, за кого он пытается себя выдать, с помощью некой уникальной информации. Данную процедуру следует отличать от идентификации (опознавания субъекта информационного взаимодействия) и авторизации (проверки прав доступа к ресурсам системы).В контексте веб-приложений, под аутентификацией обычно подразумевается проверка соответствия пользователя его учетной записи на веб-сервере с помощью имени («логина») и пароля. В качестве примера реализации подобного механизма на базе Zend Framework, мы дополним подобной проверкой базу компакт-дисков (веб-приложение, реализованное в статье «Введение в Zend Framework»).
Для этого нам понадобится:
- создать в базе данных таблицу для пользователей (и добавить в нее новую учетную запись);
- создать форму входа в систему;
- реализовать контроллер, содержащий действия для входа и выхода;
- добавить в общий фрагмент шаблонов страниц возможность выхода из системы;
- добавить проверку того, что пользователь вошел в систему, прежде чем позволять ему выполнять любые действия.
Таблица users
Первое, что нам понадобится — таблица в базе данных для хранения учетных записей пользователей. Ее схема будет выглядеть следующим образом:Поле | Тип | Null? | Опции поля |
---|---|---|---|
id | Integer | No | Primary key, Autoincrement |
username | Varchar(50) | No | Unique key |
password | Varchar(50) | No | — |
real_name | Varchar(100) | No | - |
При использовании MySQL, такую таблицу можно будет создать следующим запросом:
CREATE TABLE users (
id INT(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
real_name VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY username (username)
)
Нам понадобится добавить в нее учетную запись тестового пользователя:
INSERT INTO users (id, username, password, real_name)
VALUES (1, 'rob', 'rob', 'Rob Allen');
Запустите данные SQL-запросы с помощью любой клиентской программы для MySQL. Имя пользователя и пароль при желании можно сменить на любые другие значения.
Файл начальной загрузки
Чтобы иметь возможность отслеживать регистрацию пользователей в системе (вход и выход), нам понадобится использовать механизм PHP-сессий. Для удобной работы с ним в Zend Framework предусмотрен специальный классZend_Session_Namespace
.Нам понадобится внести следующие изменения в файл начальной загрузки:
zf-tutorial/index.php:
...
Zend_Loader::loadClass('Zend_Db_Table');
Zend_Loader::loadClass('Zend_Debug');
Zend_Loader::loadClass('Zend_Auth');
// load configuration
...
// setup database
$dbAdapter = Zend_Db::factory($config->db->adapter,
$config->db->config->asArray());
Zend_Db_Table::setDefaultAdapter($dbAdapter);
Zend_Registry::set('dbAdapter', $dbAdapter);
// setup controller
$frontController = Zend_Controller_Front::getInstance();
...
Все, что необходимо здесь выполнить, — убедиться в том, что класс
Zend_Auth
подключен, и адаптер базы данных dbAdapter
зарегистрирован. Этот адаптер будет храниться в реестре, т. к. позже понадобится иметь к нему доступ из контроллера авторизации.Контроллер авторизации
Для того, чтобы сгруппировать действия входа и выхода, нам понадобится специальный контроллер. Логично будет задать ему имяAuthController
. Начнем его реализацию с конструктора и определения действия по-умолчанию (indexAction()
):zf-tutorial/application/controllers/AuthController.php:
<?php
class AuthController extends Zend_Controller_Action
{
function init()
{
$this->initView();
$this->view->baseUrl = $this->_request->getBaseUrl();
}
function indexAction()
{
$this->_redirect('/');
}
}
В конструкторе класса-контроллера инициализируется вид и присваивается значение переменной
baseUrl
. Помимо конструктора, в классе определено действие indexAction()
, что является обязательным требованием ко всем наследникам Zend_Controller_Action
. Учитывая, что мы собираемся использовать только loginAction()
и logoutAction()
, действие по умолчанию нам не потребуется, поэтому мы будем перенаправлять пользователя с соответствующего этому действию URL на главную страницу сайта.Вход
Для того, чтобы войти в систему, нужна специальная форма. Действиеlogin
контроллера AuthController
будет взаимодействовать с ней точно так же, как действия IndexController
работают со своими формами. Шаблон формы располагается в файле views/scripts/auth/login.phtml
, а данные из нее будут обрабатываться методом AuthController::loginAction()
.Форма входа в систему достаточно проста и содержит всего два поля — для имени пользователя и его пароля.
zf-tutorial/application/views/scripts/auth/login.phtml:
<?php echo $this->render('header.phtml'); ?>
<h1><?php echo $this->escape($this->title); ?></h1>
<?php if(!empty($this->message)) :?>
<div id="message">
<?php echo $this->escape($this->message);?>
</div>
<?php endif; ?>
<form action="<?php echo $this->baseUrl ?>/auth/login" method="post">
<div>
<label for="username">Username</label>
<input type="text" name="username" value=""/>
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" value=""/>
</div>
<div id="formbutton">
<input type="submit" name="login" value="Login" />
</div>
</form>
<?php echo $this->render('footer.phtml'); ?>
Шаблон отображает
header.phtml
и footer.phtml
в начале и в конце страницы соответственно. Обратите внимание, что сообщение из переменной $this→message
выводится только в том случае, когда ее значение не пусто. Эта переменная используется, если при входе произошла ошибках и пользователю необходимо сообщить о ней. Оставшаяся часть шаблона представляет собой саму форму входа в систему.Теперь, когда наша форма готова, можем перейти к созданию контроллера для работы с ней.
zf-tutorial/application/controllers/AuthController.php:
class AuthController extends Zend_Controller_Action
{
...
function loginAction()
{
$this->view->message = '';
$this->view->title = "Log in";
$this->render();
}
}
Для отображения формы нужно задать ее заголовок и текст сообщения, после чего форму можно будет увидеть, перейдя по URL
http://zf-tutorial/auth/login
. Возникает вопрос, как в таком случае обрабатывать пересылаемые из нее данные? Для этого мы используем тот же способ, который применялся в случае с формами редактирования и добавления записей в IndexController
. То-есть обработка данных будет производиться, только если метод обращения к серверу — POST. В противном же случае, действие login
будет просто выдавать форму. Необходимые изменения в loginAction()
показаны ниже.zf-tutorial/application/controllers/AuthController.php:
class AuthController extends Zend_Controller_Action
{
...
function loginAction()
{
$this->view->message = '';
if ($this->_request->isPost()) {
// collect the data from the user
Zend_Loader::loadClass('Zend_Filter_StripTags');
$f = new Zend_Filter_StripTags();
$username = $f->filter($this->_request->getPost('username'));
$password = $f->filter($this->_request->
getPost('password'));
if (empty($username)) {
$this->view->message = 'Please provide a username.';
} else {
// setup Zend_Auth adapter for a database table
Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable');
$dbAdapter = Zend_Registry::get('dbAdapter');
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users');
$authAdapter->setIdentityColumn('username');
$authAdapter->setCredentialColumn('password');
// Set the input credential values
// to authenticate against
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
// do the authentication
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// success: store database row to auth's storage
// system. (Not the password though!)
$data = $authAdapter->getResultRowObject(null, 'password');
$auth->getStorage()->write($data);
$this->_redirect('/');
} else {
// failure: clear database row from session
$this->view->message = 'Login failed.';
}
}
}
$this->view->title = "Log in";
$this->render();
}
}
Рассмотрим приведенный выше код пошагово:
// collect the data from the user
Zend_Loader::loadClass('Zend_Filter_StripTags');
$f = new Zend_Filter_StripTags();
$username = $f->filter($this->_request->getPost('username'));
$password = $f->filter($this->_request->getPost('password'));
if (empty($username)) {
$this->view->message = 'Please provide a username.';
} else {
...
Здесь, как обычно, мы извлекаем имя пользователя и пароль из массива POST и обрабатываем их значения HTML-фильтром. Использованная при этом функция
getPost()
автоматически проверяет наличие задаваемых в ее параметре переменных и, в случае если таковые не обнаружены в POST, возвращает пустое значение.Процесс аутентификации продолжается, только если имя пользователя задано. В случае пустого значения, попытка выполнить аутентификацию через
Zend_Auth
возбудила бы исключительную ситуацию.// setup Zend_Auth adapter for a database table
Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable');
$dbAdapter = Zend_Registry::get('dbAdapter');
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users');
$authAdapter->setIdentityColumn('username');
$authAdapter->setCredentialColumn('password');
Для работы с авторизационными данными в
Zend_Auth
используется подсистема адаптеров. Такие адаптеры предоставляют унифицированный интерфейс к разнотипным хранилищам данных, таких как реляционные БД, LDAP или простые файлы. В нашем примере для этой цели будет используется база данных, поэтому выбран адаптер Zend_Auth_Adapter_DbTable
. Для того, чтобы его инициализировать, необходимо задать параметры базы данных (имя таблицы пользователей и названия ее полей).// Set the input credential values to authenticate against
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
Так же мы должны передать в адаптер точные значения имени пользователя и пароля, которые были введены в форму.
// do the authentication
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
Для того, чтобы выполнить саму процедуру аутентификации, выполняется вызов метода
authenticate()
класса Zend_Auth
. При этом результат аутентификации автоматически сохраняется в сессии.if ($result->isValid()) {
// success : store database row to auth's storage
// system. (not the password though!)
$data = $authAdapter->getResultRowObject(null,
'password');
$auth->getStorage()->write($data);
$this->_redirect('/');
В случае, если аутентификация прошла успешно, учетная запись пользователя из базы данных будет полностью сохранена внутри синглетона
Zend_Auth
(за исключением пароля, разумеется). } else {
// failure: clear database row from session
$this->view->message = 'Login failed.';
}
}
В том случае, если проверка имени пользователя и пароля не прошла, мы сообщить сообщаем об этом пользователю через переменную
message
. На этом процесс аутентификации для входа в систему завершается.Выход
Выход из системы осуществляется гораздо проще, чем вход. Все, что для этого потребуется, — очистить данные внутри синглетонаZend_Auth
. Это реализуется в действии logoutAction()
контроллера AuthController
. Таким образом, для выхода потребуется просто перейти на URL http://zftutorial/auth/logout
.zf-tutorial/application/controllers/AuthController.php:
class AuthController extends Zend_Controller_Action
{
...
function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$this->_redirect('/');
}
}
Функция
logoutAction()
на столько тривиальна, что комментировать в ней совершенно нечего.Нам понадобится предоставить пользователю специальную ссылку, перейдя по которой он смог бы выходить из веб-приложения. Проще всего сделать ее внутри шаблона
footer
. Помимо этого, мы будем сообщать пользователю его имя, для того, чтобы он был уверен, что авторизация прошла успешно. Имена пользователей хранятся в поле real_name
соответствующей таблицы базы данных, и могут быть доступны из Zend_Auth
. Первое, что понадобится сделать, — передать это значение в вид, что мы и сделаем внутри функции init()
контроллера IndexController()
.zf-tutorial/application/controllers/IndexController.php:
class IndexController extends Zend_Controller_Action
{
function init()
{
$this->initView();
Zend_Loader::loadClass('Album');
$this->view->baseUrl = $this->_request->getBaseUrl();
$this->view->user = Zend_Auth::getInstance()->getIdentity();
}
...
}
Очень удобно, что
Zend_Auth
— синглетон. Иначе в данном случае пришлось бы хранить его содержимое в реестре.Теперь нам понадобится внести изменения в файл
footer.phtml
.zf-tutorial/application/views/footer.phtml:
<?php if($this->user) : ?>
<p id="logged-in">Logged in as <?php
echo $this->escape($this->user->real_name);?>.
<a href="<?php echo $this->baseUrl ?>/auth/logout">Logout</a></p>
<?php endif; ?>
</div>
</body>
</html>
Приведенный код не содержит ничего принципиально нового. Мы используем
escape()
, чтобы убедиться в том, что имя пользователя будет корректно отображено в браузере. Значение переменной baseUrl
применяется для правильного формирования ссылки.Функции выхода из системы готова.
Защита действий
Все, что нам осталось сделать, — удостовериться в том, что никакие действия не будут недоступны до того, как пользователь зарегистрируется.zf-tutorial/application/controllers/IndexController.php:
class IndexController extends Zend_Controller_Action
{
...
function preDispatch()
{
$auth = Zend_Auth::getInstance();
if (!$auth->hasIdentity()) {
$this->_redirect('auth/login');
}
}
...
}
Функция со стандартным именем
preDispatch()
автоматически вызывается перед любым действием контроллера. С помощью метода hasIdentity()
объекта Zend_Auth
, мы проверяем, выполнен ли вход в систему. И, если это не так, перенаправляем пользователя на auth/login
.На этом работа над авторизацией завершена.
Заключение
Приведенный пример реализации функций пользовательской авторизации на базе Zend Framework достаточно прост, но стоит понимать, что уZend_Auth
существует еще множество полезных возможностей, которые можно использовать для защиты более сложных приложений с несколькими контроллерами. Не была так же затронута система авторизации, реализованная в компоненте Zend_Acl
. Последний рассчитан на использование совместно с Zend_Auth
для разграничения уровней доступа пользователей к действиям или данным, но это уже тема для отдельного разговора.Все замечания относительно оригинальной статьи вы можете отправлять ее автору по адресу rob@akrabat.com. Комментарии к русскому переводу шлите на musayev@yandex.ru.
Update: архив с примерами программ из статьи можно найти на сайте автора: zend_auth-tutorial_104.zip.