Инструменты пользователя

Инструменты сайта


limb3_2007_4:ru:tutorials:shop:step4-2

Шаг4.2 Аутентификация пользователей

Хранение данных пользователя в сессии

Что такое toolkit?

Небольшое отступление…

Некоторые объекты являются особо популярными, и зачастую требуется иметь глобальный доступ к таким объектам в рамках всей системы (приложения). К таким объектам можно отнести Запрос (Request), Ответ (Response), Пользователь (User), Подключения к базам данных (DbConnection), различные фабрики (Factory) и т.д.

Есть несколько решений:

  • Использовать паттерн Singleton (одиночка) для реализации таких объектов.
  • Складывать такие объекты в общее хранище и передавать это хранилище между всеми объектами приложения. Часто такое решение называют контекст Context.

В Limb выбран комбинированный вариант. Если просто, то мы создали только 1 одиночку, в которой хранятся все нужные в приложении объекты, и назвали ее Toolkit.

Итак, Toolkit (тулкит) - это такой объект, через который можно получить какой-либо часто используемый или важный объект в любом месте программы. Тулкит реализован через класс lmbToolkit

Для получения доступа к тулкиту используется метод lmbToolkit :: instance().

Пример использования тулкита:

 $toolkit = lmbToolkit :: instance();
 $connection = $toolkit->getDefaultDbConnection();

Особенностью тулкита является то, что его интерфейс четко не определен, а зависит от так называемых инструментов - tools. Тулкит по сути состоит из наборов инструментов, вернее tools можно регистрировать в тулките. Именно tools определяют, что умеет тулкит (какие методы у него можно вызывать). Для регистрации нового набора инструментов в тулките существует метод lmbToolkit :: merge($tools).

Например, создадим свой tools и зарегистрируем его в lmbToolkit:

class MyTools extends lmbAbstractTools
{
  function getSomethingUseful()
  { 
    return [...];
  }
}
 
lmbToolkit :: merge(new MyTools()); // :: instance() для добавления уже вызывать не нужно
 
[...]
// Где-то в клиентском коде
$toolkit = lmbToolkit :: instance();
$my_object = $toolkit->getSomethingUseful();

Наборы инструментов можно заменять, то есть мы можем зарегистировать в тулките другой tools, который будет поддерживать интерфейс MyTools и для клиентского кода эта подмена будет совершенно прозрачной.

Начальное заполнение тулкита наборами инструментов производится в файле limb/web_app/toolkit.inc.php.

Этой информации пока нам вполне достаточно, чтобы понять наши дальнейшие действия. Более подробно о тулките в описании пакета Toolkit.

Для нашего примера мы создадим наш собственный набор инструментов ShopTools и сделаем так, чтобы он поддерживал метод getUser(), который будет возвращать пользователя из сессии.

Работа с сессией в Limb

Limb не запрещает вам работать с сессиями через глобальную переменную $_SESSION, однако представляет другой, лучший с нашей точки зрения способ, со своими преимуществами.

Для работы с сессией, в Limb есть специальный объект session, который можно получить через тулкит:

 $session = lmbToolkit :: instance()->getSession();
 $session->set('my_var', $value); // Поставить значение в сессию
 $session->destroy('my_var');  // Удалить значение из сессии

Работа с сессией осуществляется при помощи пакета SESSION.

Преимущества использования пакета SESSION:

  • Возможность легкой смены способа хранения сессионных данных. На данный момент поддерживается хранение сессионных данных в базе данных и при помощи файлов.
  • Автоматическое подключение необходимых классов до десериализации сессии. Вам не нужно больше об этом заботиться.
  • Единый set/get интерфейс для установки и получения данных из сессии. Это позволяет в случае необходимости, например, передать сессию в WACT-шаблон в качестве контейнера с данными.
  • Легкое установление и сброс фикстур при модульном тестировании.

Хранение пользователя в тулките

Для того, чтобы хранить информацию о том, какой пользователь загеристрирован в приложении, нам необходимо хранить только его идентификатор. Этого будет вполне достаточно. А объект класса User мы будем хранить в ShopTools, которые мы зарегистрируем в тулките. Это позволит нам получить доступ к пользователю в любом месте программы.

Итак, класс ShopTools.

Файл shop/src/toolkit/ShopTools.class.php:

<?php
lmb_require('limb/toolkit/src/lmbAbstractTools.class.php');
lmb_require('src/model/User.class.php');
 
class ShopTools extends lmbAbstractTools
{
  protected $user;
 
  function getUser()
  {
    if(is_object($this->user))
      return $this->user;
 
    $session = lmbToolkit :: instance()->getSession();
    if($user_id = $session->get('user_id'))
    {
      try
      {
        $this->user = new User($user_id);
        $this->user->setIsLoggedIn(true);
      }
      catch(lmbARException $e)
      {
        $this->user = new User();
        $session->remove('user_id'); 
      }
    }
    else
      $this->user = new User();
 
    return $this->user;
  }
}
?>

Класс ShopUser наследуется от lmbAbstractTools, который используется в качестве базового для большинства наборов инструментов. lmbAbstractTools добавляет в тулкит поддержку всех методов класса (свои и методы дочернего класса), что нам в данном случае и нужно.

Метод getUser() по сути реализует lazy loading для пользователя. Если идентификатор пользователя находится в сессии, значит пользователь был уже залогинен в приложении - и мы загружаем его запись из базы данных. Если же идентификатора пользователя в сессии нет - значит можно создать пустой объект класса User.

Обратите внимание на блок try, catch, который ловит исключение класса lmbARException. Если пользователь с указанным удентификаторов был удален, то при инициализации User с идентификаторов, будет сгенерировано исключение lmbARException. В этом случае мы также должны инициализировать пустой объект класса User и удалить значение user_id из сессии.

Регистрация ShopTools в тулките

Наш набор инструментов ShopTools необходимо зарегистрировать в тулките. Обычно для этих целей создается файл toolkit.inc.php в корневой директории пакета или приложения.

Файл shop/toolkit.inc.php:

<?php
lmb_require('src/toolkit/ShopTools.class.php');
 
lmbToolkit :: merge(new ShopTools());
?>

Добавим включение файла toolkit.inc.php в файл common.inc.php.

Файл shop/common.inc.php:

<?php
lmb_require('limb/web_app/src/controller/lmbController.class.php');
lmb_require('limb/active_record/src/lmbActiveRecord.class.php');
 
require_once('toolkit.inc.php');
?>

Теперь у нас есть все чтобы начать реализовывать процедуру аутентификации пользователей, отображение профайла на страницах сайта и проверку прав доступа к панели управления.

Аутентификация пользователей

Изменения в классе User

Для начала внесем изменения в класс User, чтобы он хранил информацию о том, зарегистрирован ли пользователь или нет, а также функционал по аутентификации:

class User extends lmbActiveRecord
{
  [...]
  protected $is_logged_in = false;
  [...]
 
  function login($login, $password)
  {
    $hashed_password = User :: cryptPassword($password);
 
    $criteria = new lmbSQLFieldCriteria('login', $login);
    $criteria->addAnd(new lmbSQLFieldCriteria('hashed_password', $hashed_password));
 
    if($user = lmbActiveRecord :: findFirst('User', array('criteria' => $criteria)))
    {
      $this->import($user);
      $this->setIsNew(false);
      $this->setIsLoggedIn(true);
      return true;
    }
    else
    {
      $this->setIsLoggedIn(false);
      return false;
    }
  }
 
  function logout()
  {
    $this->removeAll();
    $this->is_logged_in = false;
  }
}

Контроллер LoginController

За аутентификацию пользователей будет отвечать контроллер LoginController :

<?php
lmb_require('src/model/User.class.php');
 
class LoginController extends lmbController
{
  function doDisplay()
  {
    if($this->request->hasPost())
    {
      $user = $this->toolkit->getUser();
 
      $this->useForm('login_form');
      $this->setFormDatasource($this->request);
 
      $this->_validateLoginForm();
 
      if(!$this->error_list->isValid())
        return;
 
      $login = $this->request->get('login');
      $password = $this->request->get('password');
 
      if(!$user->login($login, $password))
      {
        $this->flashError('Login or password incorrect!');
        return;
      }
 
      $this->toolkit->getSession()->set('user_id', $user->getId());
 
      $this->flashMessage('You were logged in!');
 
      if($user->getIsAdmin())
        $this->toolkit->redirect(array('controller' => 'admin'));
      else
       $this->toolkit->redirect('/');
    }
  }
 
  protected function _validateLoginForm()
  {
    $this->validator->addRequiredRule('login');
    $this->validator->addRequiredRule('password');
    $this->validator->validate($this->request);
  }
 
  function doLogout()
  {
    $user = $this->toolkit->getUser();
    $user->logout();
    $this->toolkit->getSession()->remove('user_id');
    $this->response->redirect('/');
  }
}
?>

Большинство кода должны быть уже понятным, но некоторые моменты мы поясним.

Методы flashError($message) и flashMessage($message) класса lmbController для вас новые. При помощи них можно передавать ошибки или другие сообщения в шаблон, наподобие того, как это происходит в Rails (конечно, если вы в курсе). Методы lmbController :: flashError($message) и lmbController :: flashMessage($message) на самом деле делегирует его в тулкит. Поэтому мы могли бы написать, например, $this→toolkit→flashError($message) и результат был бы тот же. Сообщения, выводимые через flashError() и flashMessage() хранятся в сесии и удаляются из нее в момент их отображения в шаблоне. Для отображения ошибок нам придется модифицировать шаблон page.html, о чем мы расскажем ниже.

Логика работы LoginController :: doDisplay() должна быть понятной: если процедура аутентификации прошла успешно, мы должны сохранить идентификатор пользователя в сессии. Аналогично, если пользователь выходит из приложения (doLogout()), мы должны удалить его идентификатор из сессии.

После аутентификации мы перебрасываем пользователя на стартовую страницу:

  • если он администратор - на /admin
  • если он не администратор - на главную страницу.

Теперь создадим шаблон login/display.html, что позволит нам завершить процедуру аутентификации пользователя.

После этого нам останется только один момент - отображать данные пользователя на страницах панели управления и выводить ссылку на выход из системы. Но для этого нам потребуются дополнительные знания, поэтому этот функционал мы добавим чуть позже.

Шаблон login/display.html

Файл shop/template/login/display.html:

<core:set title='Login'/>
<core:wrap file="page.html" in="content">
 
<form method="POST" id='login_form' runat='server'>
 
  <core:include file='form_errors.html'/>
 
  <label for='login'>Login:</label><br/>
  <input type="text" name="login" id="login" title="Login" class='input'/><br/>
 
  <label for='passwd'>Password:</label><br/>
  <input type="text" name="password" id="password" type="password" title="Password" class='input'/><br/>
 
  <input type='submit' class='button' name='submitted' value="Submit" class='button'/><br/>
</form>
 
</core:wrap>

Шаблон flash_box.html и вывод произвольных сообщений для пользователя

Создадим также шаблон flash_box.html, который будет использовать для вывода дополнительных ошибок, тех которые, например добавляются при помощи методов lmbController :: flashMessage() или lmbController :: flashError().

Файл shop/template/flash_box.html:

<flash_box target='flash_box' />
<list:list id='flash_box'>
<ul>
  <list:ITEM>
  <core:OPTIONAL for='is_error'><li class='error'>{$message}</li></core:OPTIONAL>
  <core:OPTIONAL for='is_message'><li class='message'>{$message}</li></core:OPTIONAL>
  </list:ITEM>
</div>
</list:list>

Для получения сообщений мы использовали тег <flash_box> в качестве источника данных. Каждое сообщение, полученное из в коллекции содержит поля is_error, is_message и message.

Изменения в шаблоне page.html

Внесем также минимальные изменения в page.html, где мы подключим новый шаблон flash_box.html:

[...]
<div id="center">
 
  <div id="wrapper" >
 
    <div id="container">
      <div id="content">
        <h1>{$title}</h1>
 
        <core:include file='flash_box.html'/>
 
        <core:placeholder id='content'/>
      </div>
    </div>
    [...]

Такие же изменения можно произвести с шаблоном admin_page.html, если потребуется.

Промежуточные результаты

Попробуем зайти на страницу /login нашего приложения и ввести любые данных в поля для ввода логина и пароля:

Ошибка входа в систему}

Мы можете также попробовать ввести логин и пароль учетной записи, созданной ваши ранее. В этом случае вы увидите надпись «You were logged in!».

Далее

Теперь у нас есть функционал по аутентификации пользователей, но толку от него немного. Мы должны внедрить в наше приложение проверку прав доступа к панели управления, а также отобразить информацию для текущего пользователя.

Итак, следующий шаг: Шаг4.3 Проверка прав доступа.

Обсуждение

Ваш комментарий. Вики-синтаксис разрешён:
  _   __  __  __   ____  _      __   ____
 | | / / / / / /  / __/ | | /| / /  / __/
 | |/ / / /_/ /  / _/   | |/ |/ /  / _/  
 |___/  \____/  /_/     |__/|__/  /_/
 
limb3_2007_4/ru/tutorials/shop/step4-2.txt · Последние изменения: 2010/11/10 10:02 (внешнее изменение)