Как работает базовая аутентификация
Наряду с
аутентификацией при помощи формы, существует и более простая -- так наз.
"базовая аутентификация":
- При первой попытке зайти на сайт сервер проверяет, если среди заголовков запроса поле 'Authorization'. Если нет или там содержится неверное значение, возвращается ошибка 401.
- Браузер открывает всплывающее окошко для ввода имени пользователя логин и пароля
- Пользователь вводит имя и пароль, нажимает OK и браузер делает повторный запрос, вставив в заголовок запроса: Authorization: Basic данные.
- Сервер проверяет логин с паролем и если все в порядке, возвращает запрошенную страницу с кодом 200.
Пользователей заводит администратор веб-сервера. Их имена и пароли заносятся в конфигурацию. Они могут быть зашифрованы или храниться в виде текста -- зависит от требований к безопасности.
Эта защита считается не самой надежной и применяется чаще всего в технических целях. Например, чтобы закрыть доступ к порталу всем, кроме заказчиков, разработчиков и тестировщиков, пока не закончена работа.
В одной из заметок был продемонстрирован
краулер, способный обходить сайт и проверять, все ли ссылки правильно работают. Поскольку использоваться он будет чаще всего на этапе тестирования сайтов, ему недостает умения заходить на защищенные таким способом страницы.
Базовая аутентификация и urllib2
Для базовой аутентификации средствами питоновской библиотеки
urllib2 нужно использовать два встроенных класса:
- HTTPPasswordMgr
- HTTPBasicAuthHandler
Первый позволяет задавать имя пользователя и пароль, второй добавляется в набор
handler-ов (обработчиков запроса) для специализированного экземпляра
OpenDirector (см.
подробности).
Простейший вариант HTTPPasswordMgr
class KnownPasswordMgr(HTTPPasswordMgr):
"""
Хранит заранее заданную пару логин/пароль
"""
def __init__(self, username, password):
HTTPPasswordMgr.__init__(self)
self.username = username
self.password = password
def find_user_password(self, realm, authuri):
"""
Возвращает заранее известную пару значений
"""
retval = HTTPPasswordMgr.find_user_password(self, realm, authuri)
if not (retval[0] or retval[1]):
return (self.username, self.password)
return retval
Этот класс рассчитан на использование одной единственной пары имя/пароль.
HTTPBasicAuthHandler
В конструктор класса
UserAgent добавим необязательный параметр:
credentials. Если он присутствует, к набору
handler-ов будет добавлен
HTTPBasicAuthHandler.
class UserAgent(object):
"""
Краулер.
Именованные аргументы конструктора и значения по умолчанию:
agentname -- имя ('Test/1.0')
email -- адрес разработчика (пустая строка)
hdrs -- словарь HTTP-заголовков (DEFAULT_HEADERS)
ignore_robots -- True, если следует игнорировать robots.txt (False)
credentials -- словарь с ключами 'логин' и 'пароль', если нужна
базовая аутентификация (None).
"""
def __init__(self,
agentname=DEFAULT_AGENTNAME,
email=DEFAULT_EMAIL,
hdrs=None,
ignore_robots=False,
credentials=None):
if not hdrs:
hdrs = {}
self.agentname = agentname
self.email = email
self.cookies_handler = SessionCookieHandler()
handlers = [ self.cookies_handler, ]
if not ignore_robots:
handlers.append(RobotsHTTPHandler(self.agentname))
if credentials:
handlers.append(
HTTPBasicAuthHandler(KnownPasswordMgr(**credentials))
)
self.opener = urllib2.build_opener(*handlers)
# переопределение заголовков по умолчанию
headers = copy(DEFAULT_HEADERS)
headers.update(hdrs)
op_headers = [ (k, v) for k, v in headers.iteritems() ]
op_headers.append(('User-Agent', self.agentname))
# если email не задан, HTTP-заголовок 'From' не нужен
if self.email:
op_headers.append(('From', self.email))
self.opener.addheaders = op_headers
Вот, собственно, и все.
TestCase для новой функции может выглядеть так:
class TestAuth(unittest.TestCase):
def setUp(self):
self.crawler = crawler.UserAgent(
agentname='Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
ignore_robots=True,
credentials = dict(username='ЛОГИН', password='ПАРОЛЬ')
)
def _on_success(self, *args):
self.assertTrue(1)
def _on_failure(self, url, err):
self.fail('Authentication failed: %s' % err)
def test_authentication(self):
self.crawler.visit('АДРЕС САЙТА',
on_success=self._on_success,
on_failure=self._on_failure )
Здесь, в отличие от
авторизации через форму, в случае успешного ответа не надо дополнительно проверять содержание страницы. В случае ошибки будет возвращаться все тот же код 401, т.е. функция обратного вызова _on_
success не будет вызвана.
Ярлыки: Python, urllib2, аутентификация, краулер, тесты