Pull to refresh

Введение в Zend_Auth

Reading time 14 min
Views 6.8K
Original author: Rob Allen
В статье приведен обзор возможностей компоненты 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»).

Для этого нам понадобится:
  1. создать в базе данных таблицу для пользователей (и добавить в нее новую учетную запись);
  2. создать форму входа в систему;
  3. реализовать контроллер, содержащий действия для входа и выхода;
  4. добавить в общий фрагмент шаблонов страниц возможность выхода из системы;
  5. добавить проверку того, что пользователь вошел в систему, прежде чем позволять ему выполнять любые действия.

Таблица 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.
Tags:
Hubs:
+14
Comments 34
Comments Comments 34

Articles