PHP framework CodeIgniter. Авторизация посетителей и ограничение доступа

14 февраля, 2008

Авторизация посетителей

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

Прежде всего, вкратце обрисую сложившуюся ситуацию.

Большинство web ресурсов используют для защиты пару «имя – пароль». Это не самый безопасный вариант, но зато удобный (простой) в использовании.

Более сложные методы защиты требуют от посетителя либо специальных знаний, либо покупки оборудования, либо того и другого. Например, служба WebMoney использует сертификаты для авторизации пользователей. Метод хороший, но инструкция по использованию и безопасной работе с этими сертификатами занимает несколько страниц. Обычный пользователь интернета не станет связываться с такой системой без достаточно веской причины (в случае WebMoney речь идет о его собственных деньгах).

Об аппаратной защите (вроде биометрических датчиков и т.п.) и говорить не приходиться. Эти устройства стоят денег и, зачастую, немаленьких.

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

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

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

CodeIgniter предлагает элегантное решение этой проблемы. Идея заключается в использовании метода _remap. Если в контроллере объявлен этот метод, то обращения к любому другому методу контроллера будут переадресованы ему.

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

  1. function _remap($method) {
  2.     //страницы, доступные без авторизации
  3.     $allowedPages = array('index', 'newuser', 'about');
  4.     $pars = $this->uri->segment_array();
  5.     unset($pars[1]);
  6.     unset($pars[2]);
  7.     if (($method != null) &&
  8.         (($this->session->userdata('username') != null) ||  in_array($method, $allowedPages))) {
  9.         call_user_func_array(array($this, $method), $pars);
  10.     }
  11.     else {
  12.         $this->index();
  13.     }
  14. }

Разберем подробнее код этого примера.

Прежде всего, я объявил массив ($allowedPages) с перечнем страниц (методов контроллера) доступных без авторизации. Перечень составлен произвольно и предполагается, что страница 'index' содержит форму для ввода имени и пароля.

После этого, сохранил все сегменты адреса и удалил первые два (строки 4-6). Как вы помните, в CodeIgniter первый сегмент адреса содержит имя контроллера (оно нам не нужно), второй – имя метода (передается методу _remap в параметре $method), в остальных могут передаваться параметры (вот их нужно сохранить и передать вызываемому методу).

В строках 7, 8 проверяется можно ли вызвать указанный метод. Приведенный код вызовет метод в двух случаях:
1) если в сессии был установлен параметр 'username';
2) название метода содержится в массиве $allowedPages, т.е. доступ к нему разрешен без авторизации.

Вызов метода контроллера осуществляется с помощью функции call_user_func_array, т.к. параметры метода находятся в массиве.

Если условия не выполняются, будет вызван index(). Т.е. посетитель попадет на главную страницу сайта с формой ввода имени и пароля.

Как видите, все довольно просто.

Важно. Если ваш сайт содержит несколько контроллеров, то метод _remap должен быть объявлен в каждом из них.

Ограничения

Описанный метод удобно использовать, если вам достаточно одного пользователя с административными правами.

В случае, если вам нужно создавать группы пользователей и назначать им различные привилегии, то, стоит попробовать одну из библиотек авторизации, разработанных специально для CodeIgniter. Например, FreakAuth, EzAuth, UserAuth.

Кроме того, можно использовать и другие библиотеки. Например, mihailt опубликовал очень интересную статью «Использование Zend_Acl для контроля доступа в Codeigniter’e».

Как видите, на сегодняшний день существуют решения практически для любых потребностей.

Удачи!

Понравилась статья? Подписывайтесь на продолжение rss link !

Или на мой твиттер twitter link

]]>

Добавьте эту страницу в google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru

]]>

Опубликовано в CodeIgniter, PHP Комментарии (29) »

]]>

Комментарии (29)

Вы можете отслеживать обсуждение записи с помощью RSS 2.0 rss link

Вы также можете оставить комментарий, или трекбек с Вашего сайта.

]]>
  1. Tazman

    Интересный метод. Вот только с привилегиями не очень удобно. У себя я сделал так: взял за основу simplelogin. Расширил для работы с группами, добавил 2 функции: что в return показывает выполнен ли вход, и что показывает группу пользователя и все норм. В каждой функции что требует авторицации или опред. группы (у меня всего три) я приписал проверку входа и группы (всего 4 строчки copy-paste) вроде работает :)

  2. Имхо в виде хука это выглядело бы элегантней 8-)

  3. В принципе тоже самое можно запихнуть и в конструктор или сделать хелпер в нём используя get_instance() и прописать в autoload

    для получения контроллера и его метода, лучше не обрабатывать сегменты урл, а использовать методы класса Router – fetch_class() и fetch_method()

    Erkana – вещь неплохая, правда очень сильно напоминает рапидовский Auth класс – не знаю как у них там и что но попахивает плагиатом

    Сейчас в основном использую Zend_acl и Zend_Auth чего и вам советую

  4. Хотел узнать. Есть ли где-нибудь описанные решения по интеграции авторизации в CodeIgniter и популярными форумами (наиболее интересны SMF и phpBB2).

    Пожалуйста, поделитесь ссылочками :)

    • Честно говоря, вопрос поставил меня в тупик.

      Если я правильно понял, нужно прикрутить сайт (web службу) к форуму или наоборот. При этом если посетитель авторизировался на сайте, он должен автоматически авторизироваться на форуме.

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

      Готовые решения может и существуют, но я с ними не сталкивался.

  5. Lester

    >Важно. Если ваш сайт содержит несколько
    >контроллеров, то метод _remap должен быть объявлен
    >в каждом из них.
    Сайт с одним контролером – это слишком примитивно, куда более интереснее было бы описать как организовать авторизацию на сайте с несколькими контроллерами. Я так мыслю, в этом случае надо задействовать хуки или расширять базовый контроллер. Только начинаю разбираться с CI, поэтому конкретных примеров привести пока не могу, если кто реализовывал такую авторизацию, поделитесь мыслями, примерами.

    • Чтобы сделать авторизацию на сайте с несколькими описанным методом достаточно скопировать метод _remap в конструктор каждого контроллера.
      Как правильно заметил Sam это не очень красивое решение, но оно работает.

      Еще советую попробовать библиотеки авторизации. Ссылки есть в статье и комментариях, кроме того mihailt была статья о подключении Zend_acl к CI.

  6. go

    а почему
    call_user_func_array(array($this, $method), $pars);
    в упор не пойму!
    чем это отличается от
    $this->$method($pars);
    и красивее и короче да и логичнее, вообше не пойму зачем CI делает так же у себя в ядре, передавая управлени юзерскому приложению в codeigniter.php, там тоже есть такой способ, может кто нить объяснит зачем использовать ф-цию в замен стандаартного способа?

    • Для $this->$method($pars) нужно перечислить параметры метода как при обычном вызове, а call_user_func_array принимает массив с параметрами. Это удобнее если методы принимают разное количество параметров.

  7. go

    Владмир, а вам не кажется что это откоряка? чистой воды…
    во-первых указанный мною способ визуально логичнее для восприятия, а во-вторых вместо одного вызова вы делаете два! т.е. вначале вызов call_…. а потом эта функция наверняка делает вызов нужного метода… в результате вы используете больше памяти (не намного но больше).

    • Почему откоряка?
      Может памяти и больше потребляется, но и количество кода сокращается. CI позволяет получить параметры в виде массива. Если использовать $this->$method($pars), то сначала придется проверить сколько параметров нужно передать и написать что-то вроде:
      switch ($count) {
      case 1: $this->$method($pars[0]);
      case 2: $this->$method($pars[0], $pars[1]);
      ...
      }

      И заодно проверить сколько параметров принимает данный метод.
      Вобщем сделать все то, что скорее всего делает метод call_user_func_array.

  8. go

    Если я вас првильно понимаю то переданный в call_user_func_array массив параметров в метод передадутся уже не в виде массива а так как нужно методу? если так то это действительно удобно соглашусь с вами.
    Хоия считаю что в методе на приеме можно это все отловить и усечь. Просто внешне согласитесь, ну совершенно привычней и логичней был бы обращение к методу классическим способом/

    а так вообще то _remap примнимает два параметра
    1. название метода
    2. массив параметров
    да в доках это не сказано, но зато в исходниках отчетливо видно что именно так и происходит.

    // Is there a "remap" function?
    if (method_exists($CI, '_remap'))
    {
    $CI->_remap($method,array_slice($URI->rsegments, 2));

    честно говоря я не пойму зачем в выше приведенном коде 5 и 6 строчки (в самом верху страницы)? и главное ведь данный код совершенно ни как не спасает если придет запрос на несуществующий метод )) !!
    поэтому придется либо юзать PHP5 ( я за!!! ) и там есть метод _call который сработает в случае если нет такого метода и тогда ваш вариант с call_user_func… как раз подойдет
    либо делать все равно switch
    ну либо фиксит CI и делать проверку на метод в CodeIgniter.php

    а я делал в своем проекте
    _remap и большой switch и вызывал в case'ах $this->$method($args[0]), к примеру,
    я ведь знаю что нужно моему методу )) в смысле какие параметры

    • Да, вы правильно поняли :-)
      Отсечь и уловить – это дополнительный код, а, учитывая, что есть готовая функция, это дублирование кода.
      Согласен, обращение классическим способом выглядит лучше.

      >> принимает два параметра
      Признаю, просмотрел. Код лень было смотреть, ориентировался на доку. Спасибо!

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

      >> юзать PHP5
      я не против, но почему бы не сделать просто страницу для ошибки 404 с навигацией по сайту?

      >> я ведь знаю что нужно моему методу
      конечно, знаете, но при добавлении новых методов придется вручную добавлять их в switch

  9. go

    да про добавление в switch это действительно моментик ))
    кстати вот проект сделанный на CI – proday.by
    доска ВИДЕО объявлений
    (правда видео пока никто не добавил) =-)
    там для видео мы использовали youtube API
    а нашем случае авторизацю вынесли в отдельный контроллер а в основном контроллере в конструкторе делаем простую проверку

  10. Делал похоже как у вас, но весь код вынес в отдельную либу authorize и сделал ей автолоад.

  11. Max

    А чем не нравится использование библиотеки в автолоаде? в конструкторе класса (так мы его инициализируем) делаем все нужные вещи с авторизацией – если надо – отправляем на индекс, на защащённую страницу, да хоть к чёрту на рога. При этом, совсем не обязательно теперь дублировать этот ремап в других классах (ни в одном классе, я бы даже так сказал :) ). Вот и всё :)

]]>

Оставить комментарий

* - обязательные для заполнения поля

]]>