Часто бывает нужно ограничить доступ пользователей к определенным зонам вашего сайта. Например, к административной части. Часто это делают, создавая свой механизм авторизации. Однако, существует способ защитить зоны сайта с помощью встроенных средств сервера и браузера. Простейшая авторизация носит название Apache Basic authorization. Это будет большая статья, так что приготовьтесь к основательному чтению. Но зато в ней будет все, что вам когда-нибудь понадобится для работы с этим видом авторизации.
Сначала, мы кладем на сервере файл .htaccess. В нем мы указываем, какую зону (или конкретный файл) хотим запаролить. Это может выглядеть примерно так:
AuthType Basic #тип авторизации
AuthName "Name" #имя авторизации
#местоположение файла с паролями
AuthUserFile /usr/host/mysite/.htpasswd
#пропускать любого пользователя,
#который ввел верные логин и пароль
require valid-user
Кроме того, на сайте может присутствовать файл паролей. У него простая структура:
логин:зашифрованный пароль
логин:зашифрованный пароль
...
Путь к этому файлу указывается в htaccess.
Теперь давайте предположим, что кто-то набрал адрес запароленой директории. Что при этом произойдет? А вот что:
1. Браузер просит у сервера страницу по адресу http://dayte2.com/secret.
Сервер видит, что для этой страницы установлена защита. Он посылает браузеру заголовки:
WWW-Authenticate: Basic realm="My Realm"
Status: 401 Unauthorized
HTTP-Status: 401 Unauthorized
Здесь нужно сказать, что зона (realm) — важный параметр. Если требуется вести пользователя через несколько запароленых зон, не ко всем из которых пользователь должен иметь доступ, то как раз имя зоны будет определять залогинен пользователь для этого места или еще нет.
2. Получив эти заголовки, браузер рисует на экране форму запроса логина и пароля. Если пользователь нажимает "Cancel", браузер выдает все те данные, что шли за этими заголовками.
3. Если пользователь ввел логин и пароль, то они отсылаются серверу. Тот лезет в файл с паролями и смотрит там такую пару. Если находит, то посылает в ответ ту страницу, которую просил браузер в самом начале. Если нет, то вновь посылает заголовки авторизации.
4. В случае, если пользователь ввел верные логин и пароль, а сервер ответил запрашиваемой страницей, браузер запоминает введенные логин и пароль для этой зоны.
5. Затем, если браузер посылает серверу любой (!!!) запрос, он прикрепляет к нему пару — логин и пароль. Выглядит это так:
Authorization: Basic base64(login:pass)
То есть, пара логин-пароль зашифрована в base64. Еще раз обращу внимание — логин и пароль посылается теперь с любым запросом браузера. Для картинок, файлов стилей, скриптов, favico и всего остального. По сути, для получения каждого файла, вы проходите авторизацию. Только браузер это делает за вас.
6. Сервер, когда получает запрос, проверяет, не идут ли с ним еще и логин с паролем. Если идут, то сразу проводит проверку на авторизованность.
Теперь настало время разобраться, как же вывести окно авторизации апача не силами htaccess, а силами скрипта на Perl или PHP. Я специально поставил эти два решения, на двух разных языках под один заголовок. Потому что теперь, зная механику, мы можем быстро прийти к выводу, что для появления окошка запроса авторизации, нужно лишь послать нужные заголовки. Этим и займемся.
#Perl
print "WWW-Authenticate: Basic realm=\"My Realm\"\n";
print "Status: 401 Unauthorized\n";
print "HTTP-Status: 401 Unauthorized\n";
print "Content-type: text/html\n\nCancel";
#PHP
header("WWW-Authenticate: Basic realm=\"My Realm\"");
header("Status: 401 Unauthorized");
header("HTTP-Status: 401 Unauthorized");
print "Вы нажали Cancel";
Результатом и в том и в другом случае, будет форма запроса авторизации.
Допустим, пользователь ввел логин и пароль. Они отправляются на сервер. Но тут возникает проблема. Дело в том, что когда апач получает логин и пароль, он хочет сверить их в файле паролей. Но что если логины и пароли хранятся не в файле паролей а в базе данных? В этом случае, нужно проводить авторизацию скриптом до того, как это сделает апач.
Возникает затруднение: апач не передает в переменных окружения введенные логин и пароль. То есть, получить к ним доступ из Perl проблематично. Но возможно. Самое простое решение — использовать mod_rewrite. Допишите в файл htaccess строки:
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) — [E=HTTP_CGI_AUTHORIZATION:%1]
Они добавят новую переменую окружения. Из Perl она будет видна как $ENV{HTTP_CGI_AUTHORIZATION}. Она будет содержать пару логин-пароль, закодированную в base64. Конечно, придется немного повозиться с тем, чтобы их перекодировать обратно, но это уже кое-что. Тем более, что возни там, собственно, не много:
$ENV{HTTP_CGI_AUTHORIZATION} =~ s/basic\s+//i;
my ($REMOTE_USER,$REMOTE_PASSWD) = split(/:/,decode_base64($ENV{HTTP_CGI_AUTHORIZATION}));
Теперь у нас есть две переменные $REMOTE_USER и $REMOTE_PASSWD, с помощью которых можно проводить авторизацию силами скрипта, сверяя логин и пароль с чем душе угодно.
В PHP с перехватом данных авторизации все обстоит значительно проще, чем в Perl. Хитрый пыхпых самостоятельно проделывает за вас всю работу, предоставляя в ваше распоряжение две переменные: $PHP_AUTH_USER и $PHP_AUTH_PW.
Кроме всего прочего, после того, как авторизация прошла успешно, в переменной окружения будет находиться логин авторизованного пользователя, который вызывает исполняемый скрипт. То есть, вы всегда будете знать кто именно сейчас запустил скрипт. Логин содержится в переменных:
#perl
$ENV{REMOTE_USER}
#php
$_SERVER[REMOTE_USER]
Большая получилась статья, как я и обещал. Но зато в ней рассмотрены основные принципы работы апачевой base-автроизации, а так же собраны решения некоторых проблем, которые могут возникнуть при работе с ней. Скажу честно, провел не один час, в поисках ответов на некоторые вопросы, решенные в этой статье. Надеюсь, она поможет сэкономить время другим программистам.