Навигация

Вход

Список форумов -> Perl development -> HTTP-авторизация и Catalyst

  
Catalyst, HTTP авторизация и FastCGI

Sharypov

Описанный ранее способ использования http-авторизации в Catalyst проектах упорно не хотел работать с FastCGI протоколом. Использовалось:

  1. Веб-сервер apache версии 2.2.x.
  2. Модуль подержки протокола FactCGI для веб-сервера apache -- mod_fascgi версии 2.4.2. Кстати, mod_fastcgi для apache 2.2 без небольшого патча не собирается.
  3. Движок Catalyst для обработки запросов по протоколу FastCGI -- Catalyst::Engine::FastCGI.

В итоге оказалось две причины:

  1. По протоколу CGI http-заголовок Authorization должен передаваться внешнему приложению в качестве одноименной переменной окружения. Почему-то модуль mod_fastcgi по-умолчанию этого не делает, однако предусматривает специальную опцию -pass-header при передаче запроса FastCGI обработчику. Пример использования в файле конфигурации apache -- httpd.conf:

    FastCgiServer                           \
        /root/MyApp/script/myapp_fastcgi.pl \
        -pass-header Authorization
  2. После этого стала доступна переменная окружения Authorization, однако плугин Authentication::Credential::HTTP вс? ещ? не работал! Дальше пришлось залезть в исходники библиотек Catalyst. Ошибка обнаружилась не в движке Catalyst::Engine::FastCGI, а в его базовом классе Catalyst::Engine::CGI. Из чего с увереностью можно сказать, что и движок Catalyst::Engine::CGI некорректно работает с http-авторизацией. После внесения следующих исправлений вс? встало на свои места:

    @@ -95,7 +95,7 @@
     
         # Read headers from %ENV
         while ( my ( $header, $value ) = each %ENV ) {
    
    -        next unless $header =~ /^(?:HTTP|CONTENT|COOKIE)/i;
    +        next unless $header =~ /^(?:HTTP|CONTENT|COOKIE|Authorization)/i;
             ( my $field = $header ) =~ s/^HTTPS?_//;
             $c->req->headers->header( $field => $value );
         }

На мо? сообщение в список рассылки Catalyst не последовало никакой реакции и ошибка сохраняется до сих пор -- версия Catalyst::Runtime 5.7010 на текущий момент.

Сообщение создано: 18.08.2009 11:38, обновлено: 24.02.2010 12:38
 
HTTP-авторизация и Catalyst

Sharypov

Протокол HTTP предоставляет возможность авторизации пользователя по имени и паролю. Вкратце схема работы выглядит так:

  1. Веб-браузер посылает обычный http-запрос на доступ к запороленному ресурсу на веб-сервере. Пример:

    GET / HTTP/1.1
    ...
  2. Веб-сервер или веб-приложение формируют http-ответ с кодом 401 и требованием авторизации. Пример basic авторизации:

    HTTP/1.0 401 Unauthorized
    WWW-Authenticate: Basic realm="need authorization"
    ...
  3. Веб-браузер отображает окно авторизации. Например так:

    И после ввода данных посылает второй запрос к веб-ресурсу, содержащий base64-кодированный заголовок Authorization:

    GET / HTTP/1.1
    Authorization: Basic YWRtaW46c2VjcmV0
    ...
  4. Веб-приложение проверяет правильность имени и пароля. Если проверка успешна, выдает нормальный http-ответ:

    HTTP/1.0 200 OK
    ...

    Если проверка не успешна, то повторяется http-ответ с кодом 401. При этом в теле http-ответа уже может содержаться дополнительная информация, объясняющая причину отказа.

Про http-авторизацию можно почитать в rfc 2068. Или в русском переводе этого rfc. Механизм http-авторизации не лишен недостатков. Подробно они описаны в статье "Базовая HTTP-авторизация -- защита от честных людей" Алексея Мичурина, опубликованной в журнале "Системный администратор" №5 за 2005 г.

Мы рассмотрим использование http-авторизации c perl веб-фреймворком Catalyst. Для Catalyst существует плугин Catalyst::Plugin::Authentication реализующий основу любой авторизации. Но чтобы его использовать нужны ещ? два плугина:

  1. Credential -- метод получения информации о пользователе. Доступны такие:

    • Password -- авторизация по логину и паролю полученному, например, через веб-форму.
    • HTTP -- получение информации о пользователе через http-авторизацию. Именно этим плугином и воспользуемся.
    • Прочие.
  2. Store -- способ хранения и проверки полученной информации. Возможные хранилища:

    • DBIC -- хранение информации о пользователях в базе данных, с доступом к ней через ORM, например DBIx::Class.
    • Htpasswd -- излечение информации из htpasswd файлов, создаваемых одноименной утилитой, входящей в комплект веб-сервера apache. Воспользуемся этим плугином.
    • Minimal -- данные о пользователях хранятся внутри конфигурации Catalyst-проекта.
    • Прочие.

Итак, нам будут нужны следующие установленные perl-модули, естественно, со всеми зависимостями:

  • Catalyst::Devel
  • Catalyst::Plugin::Authentication
  • Catalyst::Plugin::Authentication::Credential::HTTP
  • Catalyst::Plugin::Authentication::Store::Htpasswd

Создаем заготовку проекта на фреймворке Catalyst с названием MyApp.

root@darkstar:~# catalyst.pl MyApp
created "MyApp"
created "MyApp/script"
created "MyApp/lib"
created "MyApp/root"
created "MyApp/root/static"
created "MyApp/root/static/images"
created "MyApp/t"
created "MyApp/lib/MyApp"
created "MyApp/lib/MyApp/Model"
created "MyApp/lib/MyApp/View"
created "MyApp/lib/MyApp/Controller"
created "MyApp/myapp.yml"
created "MyApp/lib/MyApp.pm"
created "MyApp/lib/MyApp/Controller/Root.pm"
created "MyApp/README"
created "MyApp/Changes"
created "MyApp/t/01app.t"
created "MyApp/t/02pod.t"
created "MyApp/t/03podcoverage.t"
created "MyApp/root/static/images/catalyst_logo.png"
created "MyApp/root/static/images/btn_120x50_built.png"
created "MyApp/root/static/images/btn_120x50_built_shadow.png"
created "MyApp/root/static/images/btn_120x50_powered.png"
created "MyApp/root/static/images/btn_120x50_powered_shadow.png"
created "MyApp/root/static/images/btn_88x31_built.png"
created "MyApp/root/static/images/btn_88x31_built_shadow.png"
created "MyApp/root/static/images/btn_88x31_powered.png"
created "MyApp/root/static/images/btn_88x31_powered_shadow.png"
created "MyApp/root/favicon.ico"
created "MyApp/Makefile.PL"
created "MyApp/script/myapp_cgi.pl"
created "MyApp/script/myapp_fastcgi.pl"
created "MyApp/script/myapp_server.pl"
created "MyApp/script/myapp_test.pl"

Чтобы убедится что вс? прошло успешно, запустим проект на встроенном в Catalyst тестовом веб-сервере.

root@darkstar:~# MyApp/script/myapp_server.pl
[debug] Debug messages enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
| Catalyst::Plugin::ConfigLoader  0.14                                       |
| Catalyst::Plugin::Static::Simple  0.19                                     |
'----------------------------------------------------------------------------'

[debug] Loaded dispatcher "Catalyst::Dispatcher"
[debug] Loaded engine "Catalyst::Engine::HTTP"
[debug] Found home "/root/MyApp"
[debug] Loaded Config "/root/MyApp/myapp.yml"
[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class                                                           | Type     |
+-----------------------------------------------------------------+----------+
| MyApp::Controller::Root                                         | instance |
'-----------------------------------------------------------------+----------'

[debug] Loaded Private actions:
.----------------------+--------------------------------------+--------------.
| Private              | Class                                | Method       |
+----------------------+--------------------------------------+--------------+
| /default             | MyApp::Controller::Root              | default      |
| /end                 | MyApp::Controller::Root              | end          |
'----------------------+--------------------------------------+--------------'

[info] MyApp powered by Catalyst 5.7007
You can connect to your server at http://darkstar:3000

Как видно, вс? замечательно работает. Теперь мы можем обращаться на 3000 порт тестового сервера из браузера и видеть приветственную заставку по умолчанию.

Начинаем прикручивать http-авторизацию. Открываем основной модуль приложения /MyApp/lib/MyApp.pm, находим строчку, отвечающую за загрузку плугинов:

use Catalyst qw/-Debug ConfigLoader Static::Simple/;

И меняем е? на:

use Catalyst qw/
    -Debug

    ConfigLoader
    Static::Simple

    Authentication
    Authentication::Store::Htpasswd
    Authentication::Credential::HTTP
/;

Добавляя таким образом загрузку необходимых плугинов авторизации. Теперь изменим файл конфигурации проекта. Открываем /MyApp/myapp.yml и вставляем следующие строчки:

---
name: MyApp

authentication:
  htpasswd: /root/MyApp/myapp.htpasswd
  http:
    type: basic

Это файл в странном формате YAML, так что соблюдение всех отступов обязательно. Зато он людьми воспринимается легче чем, например, XML. Желающие изучить вопрос могут начать с последней версии YAML спецификации :-) . Для проверки синтаксиса *.yml файла можно использовать утилиту ysh из perl модуля YAML.pm:

root@darkstar:~# cat MyApp/myapp.yml | ysh
$VAR1 = {
  'name' => 'MyApp',
  'authentication' => {
    'htpasswd' => '/root/MyApp/myapp.htpasswd',
    'http' => {
      'type' => 'basic'
    }
  }
};

Если проверка завершается успешно, то на выходе видим соответствующую perl структуру данных. Далее создаем htpasswd файл:

root@darkstar:~# htpasswd -c /root/MyApp/myapp.htpasswd admin
New password:
Re-type new password:
Adding password for user admin
root@darkstar:~# cat /root/MyApp/myapp.htpasswd
admin:mLT84dDiy3H7U

Теперь для авторизированного доступа к определенному action в него достаточно добавить одну строчку кода. Например для доступа ко всем action контроллера Root.pm используем специальный action -- auto. Добавим следующие строчки к файлу MyApp/lib/MyApp/Controller/Root.pm:

sub auto : Private {
    my ( $self, $c ) = @_;
    $c->authorization_required( realm => 'catalyst http authorization' );
}

Вс?, задача выполнена. Опять запускаем тестовый web-сервер:

root@darkstar:~# MyApp/script/myapp_server.pl
[debug] Debug messages enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
| Catalyst::Plugin::Authentication  0.10002                                  |
| Catalyst::Plugin::Authentication::Credential::HTTP  0.09                   |
| Catalyst::Plugin::Authentication::Store::Htpasswd  0.02                    |
| Catalyst::Plugin::ConfigLoader  0.14                                       |
| Catalyst::Plugin::Static::Simple  0.19                                     |
'----------------------------------------------------------------------------'

[debug] Loaded dispatcher "Catalyst::Dispatcher"
[debug] Loaded engine "Catalyst::Engine::HTTP"
[debug] Found home "/root/MyApp"
[debug] Loaded Config "/root/MyApp/myapp.yml"
[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class                                                           | Type     |
+-----------------------------------------------------------------+----------+
| MyApp::Controller::Root                                         | instance |
'-----------------------------------------------------------------+----------'

[debug] Loaded Private actions:
.----------------------+--------------------------------------+--------------.
| Private              | Class                                | Method       |
+----------------------+--------------------------------------+--------------+
| /default             | MyApp::Controller::Root              | default      |
| /end                 | MyApp::Controller::Root              | end          |
| /auto                | MyApp::Controller::Root              | auto         |
'----------------------+--------------------------------------+--------------'

[info] MyApp powered by Catalyst 5.7007
You can connect to your server at http://darkstar:3000

Обратите внимание на лог обработки http-запросов:

[info] *** Request 1 (0.143/s) [5722] [Wed Aug 29 12:52:36 2007] ***
[debug] "GET" request for "/" from "172.16.197.1"
[debug] Checking http basic authentication.
[info] Request took 0.015420s (64.851/s)
.----------------------------------------------------------------+-----------.
| Action                                                         | Time      |
+----------------------------------------------------------------+-----------+
| /auto                                                          | 0.002696s |
| /end                                                           | 0.000278s |
'----------------------------------------------------------------+-----------'

[info] *** Request 2 (0.125/s) [5722] [Wed Aug 29 12:52:45 2007] ***
[debug] "GET" request for "/" from "172.16.197.1"
[debug] Checking http basic authentication.
[debug] Successfully authenticated user 'admin'.
[info] Request took 0.025378s (39.404/s)
.----------------------------------------------------------------+-----------.
| Action                                                         | Time      |
+----------------------------------------------------------------+-----------+
| /auto                                                          | 0.013852s |
| /default                                                       | 0.001557s |
| /end                                                           | 0.000250s |
'----------------------------------------------------------------+-----------'

Первый запрос "Request 1" ещ? не содержит информации об авторизации. Action обработчик default так и не был выполнен. Запрос "Request 2" уже содержит заполненный http-заголовок Authorization. По этим данным успешно авторизуется пользователь admin. Управление передается action-у, отвечающему за запрошенный урл -- default.

Фактически вс? программирование свелось к написанию одной строчки кода. Все остальные действия это установка perl-модулей, внесение изменений в существующие конфигурационные файлы и создание новых конфигурационных файлов. Т.е. это действия системного администратора, а не программиста. Это есть одна из основных и моя самая любимая черта веб-фреймворка Catalyst.

Сообщение создано: 18.08.2009 11:36, обновлено: 24.02.2010 12:40
Вы можете только просматривать содержимое форума.