Авторизация с использованием Zend_Auth
В этой статье как обычно рассматривается использование одного из компонент зенд фреймворка, за исключением небольшого отклонения от плана: до этого момента в рубрике "Разработка приложения с помощью ZF" к каждой статье прикреплялся архив с исходным кодом демо-приложения, того что получалось в результате работы с компонентами - теперь так не будет, теперь будем рассматривать компоненты отдельно друг от друга, т.к. оказалось, что в рамках 2Developers это мало кому нужно. А по поводу Zend Framework CMS, мы начали открытый проект по её разработке с public SVN-ом и все такое, детали будут освещены позже, в следующих статьях.
И так, авторизация с помощью Zend_Auth, функция "запомнить меня" и установка длительности сессии (времени залогиненности пользователя).
Таблица пользователей в БД
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL auto_increment,
`login` varchar(36) NOT NULL,
`pass` varchar(36) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `login` (`login`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Контроллер
Добавим 2 акшена в контроллер, например это будет UserController:
<?php
require_once 'Zend/Controller/Action.php';
class UserController extends Zend_Controller_Action
{
public function loginAction()
{
// Если отправлены данные
if(isset($_POST['login']) && !empty($_POST['login'])) {
// Подключиться к базе и получить адаптер
require_once 'Zend/Db.php';
$db_adapter = Zend_Db::factory('pdo_mysql',
array('host' => 'localhost',
'username' => 'test',
'password' => 'test',
'dbname' => 'test'
)
);
// Получить модель
require_once(APPPATH."/models/User.php");
$user_model = new User($db_adapter);
if (true === $message = $user_model->login($_POST['login'], $_POST['pass'])) {
// Успешно
$this->_redirect('/');
} else {
$this->view->assign('errors', $message);
}
}
}
//__________________________________________________________
public function logoutAction()
{
// отключение скрипта вида и layout'a
$this->_helper->viewRenderer->setNoRender(true);
Zend_Layout::getMvcInstance()->disableLayout();
require_once 'Zend/Auth.php';
$auth = Zend_Auth::getInstance();
// Удалить из сессии информацию
$auth->clearIdentity();
Zend_Session::forgetMe();
$this->_redirect('/');
}
//__________________________________________________________
Как вы могли заметить, основное действие и работа с компонентом Zend_Auth происходит в модели. Здесь только получем результат, если успешно, тогда модель вернет true, в противном случае сообщение об ошибке.
Модель
<?php
require_once 'Zend/Db/Table/Abstract.php';
class User extends Zend_Db_Table_Abstract
{
protected $_name = 'users';
protected $_primary = array('login');
public function login($login, $pass)
{
require_once 'Zend/Auth.php';
// Получить экземпляр Zend_Auth
$auth = Zend_Auth::getInstance();
require_once 'Zend/Auth/Adapter/DbTable.php';
// Создаем Adapter для Zend_Auth, указывая ему где в БД искать логин и пароль для сравнения
$authAdapter = new Zend_Auth_Adapter_DbTable($this->getAdapter(), 'users', 'login', 'pass', "MD5(?)");
$authAdapter->setIdentity($login)
->setCredential($pass);
// Проверяет и сохраняет результат проверки
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
// Успешно
// Можно записать в сессию некоторые поля
$auth->getStorage()->write($authAdapter->getResultRowObject(array('id', 'login')));
// Получить объект Zend_Session_Namespace
require_once('Zend/Session/Namespace.php');
$session = new Zend_Session_Namespace('Zend_Auth');
// Установить время действия залогинености
$session->setExpirationSeconds(24*3600);
// если отметили "запомнить"
if (isset($_POST['rememberme'])) {
Zend_Session::rememberMe();
}
return true;
}
// Неудачно
return $error_msg = $result->getMessages();
}
}
Здесь есть несколько моментов, не описаных в мануале ZF, на которые следует обратить внимание.
Во-первых использование четвертого параметра при получении Auth адаптера - функция, которая применяется к паролю. Можно дбавить salt, а можно вообще хранить его в открытом виде и не передавать этот параметр, но вы наверняка так не поступите ;).
Второе это время жизни сессии часто необходимо что бы было больше, чем кажется 15 минут по умолчанию.
Третья "запомнить меня", сохраняет куки в браузере клиента.
Скрипт вида
<form action="<?php echo $this->url(); ?>" method="post">
<fieldset>
<legend>Вход</legend>
<?php if (sizeof($this->errors)) echo $this->formErrors($this->errors).'<br />'; //Вывод ошибок маркированным списком ?>
<label for="login">Логин</label>
<input type="text" id="login" name="login" value="<?php if (isset($_POST['login'])) echo $_POST['login'];?>" />
<br /><br />
<label for="pass">Пароль</label>
<input type="password" id="pass" name="pass" value="<?php if (isset($_POST['pass'])) echo $_POST['pass'];?>" />
<br /><br />
<label for="rememberme">Запомнить меня</label>
<input type="checkbox" id="rememberme" name="rememberme" value="1" />
<br /><br />
<input type="submit" value="Войти" />
<br /><br />
</fieldset>
</form>
Кажется это и все, для базового использования.
Комментарии
Чтобы сделать редирект отключать лэйаут и скрипт вида вовсе не нужно. До них дело не дойдет всё равно.
С точки зрения ООП и ухода от суперглобальных массивов вместо $_POST и всяких isset надо пользоваться методами объекта запроса. Например
$request = $this->getRequest(); // в контроллере получили объект запроса
if ($request->isPost()) { // так можно проверить был ли это пост запрос
$post = $request->getPost() // - так можно все данные взять что в пост запросе пришли
$itemsPerPage = $request->getPost('itemsPerPage'); // так можно получить какую то переменную, причем если её нет, то вернется null
Ну и контроллеры конеш толстоваты, но в рамках учебного материала это не так важно.
Хорошее описание для базового понимания.
По поводу объекта запроса, интересное замечание спасибо, ну а насчет толстоватости контроллера, если подключение к базе убрать, что там останется? Конечно же это для наглядности.
В данном примере не мешало бы использовать Zend_Form.
HighAley, я например, наоборот стараюсь воздержаться от использования Zend_Form особенно при создании сайтов, я еще не знаю такого дизайнера, который разобрался с декораторами и тому подобным. Понятно, если система электронного документооборота или какая нибудь админка сложная, Zend_Form может быть необходимым, но форма авторизации... она ведь одна такая на все приложение, мне кажется это слишком, особенно если вспомнить основную причину для применения Zend_Form.
Зехр гут! Особенно про римемберМи
У меня вопрос по поводу
Zend_Session::rememberMe() - этот метод устанавливает время жизни +2 недели длякукиPHPSESSID, т.е. для идентификатора сессии, которая умрет через24*3600, т.е. через сутки.PHPSESSIDКак же тогда сработает авторизация через пару дней?!
На самом деле, помимо
я ожидал увидеть в куках какой-нить хеш или сериализованный массив с информацией о пользователе.
В чем я заблуждаюсь?!
А ведь точно, вы ни в чем не заблуждаетесь, я упустил. Действительно ведь и на этом сайте не запоминает более, чем на сутки т.к. сделано так же. А я не замечал.
Значит нужно сделать проверку на то установлен ли параметр "запомнить" при логине,
и исходя из этого уже устанавливать время жизни сессии на сервере.
Если "запоминать" не нужно, то оставить как сейчас, к примеру сутки, а если нужно запомнить, то установить время жизни 24*3600*14.
Спасибо, что отметили этот недочет. Странно в мануале ZF никак не упоминается эта зависимость.
Тут есть ещё один неприятный для меня момент - это хранении самой сессии такое большое кол-во времени. Например, возможен вариант, что хостер не даст редактировать связанные с этим настройки в php.ini и т.д. и т.п.
Выходом конечно может быть хранение сессий в таблице БД.
Вообще, для реализации "remember me" , мне самому больше нравится вариант с сохранением зашифрованной инфы в куках, ключ к которой вшит где-нибудь в конфигах приложения :)