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

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


limb3_2007_4:ru:tutorials:shop:step4-3

Шаг4.3 Проверка прав доступа

Выбор способа реализации проверки

Проверку, может ли пользователь иметь доступ к тому или иному контроллеру, мы можем реализовать 2-мя способами:

  • Создать базовый класс контроллера нашего приложения, например, BaseShopController и расширить у него метод performAction(). А в этом методе проверять, обладает ли текущий пользователь соответствующими правами, если имя контроллера имеет Admin префикс.
  • Сделать специальный класс - фильтр, который мы прозрачно встроим в логику работы нашего приложения. Это позволит нам познакомиться с механизмом огранизации приложения, выполненного на базе Limb в целом, а также позволит не затрагивать никакие уже созданные контроллеры. В этом фильтре мы будем делать аналогичные первому способу проверки.

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

Общая схема организации приложения на базе Limb

Цепочка фильтров приложения

Взглянем на файл shop/www/index.php. Если вы помните, то именно на этот файл index.php перенаправляются все запросы к нашему приложению.

<?php
require_once(dirname(__FILE__) . '/../setup.php');
require_once('limb/web_app/src/lmbWebApplication.class.php');
 
$application = new lmbWebApplication();
$application->process();
?>

index.php по сути подключает настроечный файл setup.php и запускает объек класса lmbWebApplication. lmbWebApplication - это центральный класс, само приложение. С него начинается обработка запроса пользователя.

Рассмотрим, что представляет из себя lmbWebApplication:

Файл limb/web_app/src/lmbWebApplication.class.php:

<?php
lmb_require('limb/filter_chain/src/lmbFilterChain.class.php');
lmb_require('limb/core/src/lmbHandle.class.php');
 
class lmbWebApplication extends lmbFilterChain
{
  function __construct()
  {
    $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbUncaughtExceptionHandlingFilter'));
    $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbSessionStartupFilter'));
    $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbRequestDispatchingFilter',
                                        array(new lmbHandle('limb/web_app/src/request/lmbRoutesRequestDispatcher'))));
    $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbResponseTransactionFilter'));
    $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbActionPerformingFilter'));
    $this->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbViewRenderingFilter'));
  }
}
?>

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

Кратко, что такое lmbHandle - это класс, который используется для отложенной инициализации объектов. Это позволяет экономить на включении и парсинге php-кода в случаях, когда до некоторых объектов «не доходит очередь». Подробнее о классе lmbHandle.

Теперь пару слов о том, что такое цепочка фильтров. Если вы знакомы с шаблоном проектирования Intercepting Filter, то понять идею не составит большого труда.

Итак, фильтры являются своеобразными расширениями ядра системы, их них состоит самая «верхняя» (начальная) часть приложения, созданного на базе Limb. Фильтры выполняют действия, характерные для большинства приложений, но обычно не имеющих никакого отношения к предметной области приложения (различные вспомогательные действия). Например, фильтры могут стартовать сессию, разбирать запрос, проверять права доступа, выдавать закешированный вариант страницы, регистрировать посещение страницы в статистике и т.д. То есть фильтры - это своеобразный FrontController, только в Limb он не имеет формы единого класса, а разделен на составляющие, любую из которых можно заменить. Количество фильтров различно от приложения к приложению.

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

Цепочка фильтров реализована в виде пакета FILTER_CHAIN.

Большинство стандартных фильтров можно найти в пакете WEB_APP limb/web_app/src/filter. Вот список таких фильтров c небольшими описаниями:

ФильтрНазначение
lmbResponseTransactionFilterОтсылает данные (заголовки и контент) браузеру после того, как большинство остальных фильтров отработали. Необходим, так как логика приложения может требовать обнуления промежуточно сформированных данных и передачи совсем другого результата браузеру.
lmbSessionStartupFilterНастраивает драйвер хранилища сессионных данных и стартует сессию
lmbUncaughtExceptionHandlingFilterПозволяет обрабатывать ошибки, возникшие в системе и выводить пользователю более «мягкие варианты»
lmbRequestDispatchingFilterВ этом фильтре определяется текущий контроллер и действие.
lmbActionPerformingFilterЗапускает команду, которая соответствует текущему контроллеру и действию
lmbViewRenderingFilterЗапускает процесс рендеринга шаблона, который был установлен в результате выполнения действия контроллера

Разбор запроса, связь Контроллера (Controller) и Отображения (View)

Остался один момент - как это все собирается в единое целое:

  • как приложение определяет, что необходимо делать?
  • как осуществляется отработка нужного действия?
  • как осуществляется рендеринг шаблона?
  • как различные компоненты знают друг о друге?

За первые три пункта этого списка, как вы наверное уже догадались, отвечают соответствующие фильтры:

  • lmbRequestDispatchingFilter
  • lmbActionPerformingFilter
  • lmbViewRenderingFilter

Сменив или расширив любой из этих фильтров, мы можем изменить любой компонент Limb-приложения:

  • Сменить шаблонную систему
  • Поменять механизм разбора запроса
  • Добавить любые дополнительные проверки, характерные для всех (большинства) вызовов.

Теперь последний вопрос: как различные компоненты знают друг о друге? Для этих целей существует тулкит!

Тулкит содержит:

  • переменную $view, которая является объектом класса lmbView (вернее производного от него - скорее всего это будет lmbWactView). $view - является посредником между реальным шаблоном и приложением.
  • переменную $dispatched_controller, которая является объектом класса lmbController (или дочерним).
  • объект $request (класс lmbHttpRequest, находится в пакете NET) - представляет из себя контейнер со всеми данными, поступившими в зачестве запроса приложению.
  • объект $response (класс lmbHttpResponse, находится также в пакете NET) - представляет из себя ответ приложения на запрос.

Так как тулкит может быть получен в любом месте системы - значит все эти объекты также доступны везде в приложении!

Поэтому:

  • lmbRequestDispatchingFilter - использует $request и ставит в тулкит $dispatched_controller.
  • lmbActionPerformingFilter - получает из тулкита $dispatched_controller и вызывает метод performAction()
  • Контроллер где-то внутри получает $view и ставит в него название шаблона через метод setTemplate($name) (может также передавать во $view некоторые данные)
  • lmbViewRenderingFilter - получает $view и вызывает у мего метод render(), а результат рендеринга помещает в $response.

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

Создание фильтра для проверки прав доступа AccessFilter

Класс фильтра должен реализовывать интерфейс lmbInterceptingFilter (limb/filter_chain/src/lmbInterceptingFilter.interface.php).

Приведем код AccessFilter:

Файл shop/src/filter/AccessFilter.class.php:

<?php
lmb_require('limb/filter_chain/src/lmbInterceptingFilter.interface.php');
 
class AccessFilter implements lmbInterceptingFilter
{
  function run($filter_chain)
  {
    $toolkit = lmbToolkit :: instance();
    $user = $toolkit->getUser();
    $controller = $toolkit->getDispatchedController();
 
    if(!$controller)
      throw new lmbException('Controller is not dispatched yet!');
 
    if(!$this->_isAdminPage($controller) || ($user->getIsLoggedIn() && $user->getIsAdmin()))
    {
      $filter_chain->next();
      return;
    }
    else
      $toolkit->renderView('not_allowed.html');
  }
 
  protected function _isAdminPage($controller)
  {
    return ($controller->getName() == 'admin') ||
           (strpos($controller->getName(), 'admin_') !== false);
  }
}
?>

Наш AccessFilter реализован следующим образом:

  • он получает текущий контроллер и пользователя
  • если имя контроллера не содержит префикс admin_, тогда управление сразу передается в следующий фильтр
  • в противном случае проверяется, зарегистирован пользователь и имеет ли он флаг is_admin:
    • если нет - ему отображается страница not_allowed.html
    • если да - тогда управление передавается следующему фильтру

Для отображения сообщения о том, что страница недоступна мы также создали шаблон not_allowed.html.

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

<core:set title='Restricted page'/>
<core:wrap file="page.html" in="content">
<dl class='restricted'>
  <dt>
    <img src='/images/stop.gif'/>
    This page is restricted for you.</dt>
  <dd>Try to login as an administrator if you are not logged in yet.</dd>
</dl>
</core:wrap>

Так, теперь фильтр и шаблон у нас есть - необходимо внедрить его в наше приложение. Для этого нам придется вмешаться в файл shop/www/index.php

Создание частной цепочки фильтров приложения

Мы не будем создавать новый класс цепочки фильтров, а просто перепишем файл shop/www/index.php следующим образом:

<?php
require_once(dirname(__FILE__) . '/../setup.php');
 
lmb_require('limb/filter_chain/src/lmbFilterChain.class.php');
lmb_require('limb/core/src/lmbHandle.class.php');
 
$application = new lmbFilterChain();
 
$application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbUncaughtExceptionHandlingFilter'));
$application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbSessionStartupFilter'));
$application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbRequestDispatchingFilter',
                                    array(new lmbHandle('limb/web_app/src/request/lmbRoutesRequestDispatcher'))));
$application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbResponseTransactionFilter'));
$application->registerFilter(new lmbHandle('src/filter/AccessFilter'));
$application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbActionPerformingFilter'));
$application->registerFilter(new lmbHandle('limb/web_app/src/filter/lmbViewRenderingFilter'));
 
$application->process();
?>

Обратите внимание, что мы поместили наш фильтр AcessFilter после lmbResponseTransactionFilter. Это необходимо, так как lmbResponseTransactionFilter занимается непосредственной отсылкой ответа приложения браузеру. Вернее lmbResponseTransactionFilter завершает так называемую транзакцию класса lmbHttpResponse.

lmbHttpResponse аккумулирует все данные, которые должны были бы поступить браузеру, до самого последнего момента, и отсылает их только если вызывается метод lmbHttpResponse :: commit(). Это позволяет полностью очистить содержимое lmbHttpResponse в любое момент и начать формировать другой ответ.

Предварительные итоги

Попробуйте теперь вновь зайти на страницу /admin_product, не проходя при этом процедуры аутентификации. Если вы все сделали правильно, то увидете следующую страницу:

страница недоступна}

Далее

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

Итак, следующий шаг: Шаг4.4 Отображение профайла пользователя

Обсуждение

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