Авторизация и аутентификация. Session-based authentication

Курс по Django: https://stepik.org/a/183363

Смотреть материал на YouTube

После того, как мы с вами познакомились с управлением доступа через классы permissions, пришло время узнать, а как можно выполнять авторизацию и аутентификацию пользователей через API-запросы. Я напомню, что авторизация – это когда пользователь входит в систему, обычно по логину и паролю и сервер идентифицирует этого пользователя. А при обращении к закрытым страницам этого сайта (например, личному кабинету) сервер выполняет аутентификацию пользователя, то есть, проверяет, вошел пользователь в систему или это обычный сторонний посетитель, которому не следует давать доступ. Таким образом, процессы авторизации и аутентификации пользователей тесно связаны между собой.

Django REST Framework поддерживает несколько способов авторизации. Среди встроенных и поставляемых с самим пакетом – это авторизация на основе сессий и токенов. Дополнительно в проектах нередко используют авторизацию на основе токенов с помощью пакета Djoser, JWT (JSON Web Token) авторизацию, ну и, конечно же, при необходимости авторизацию через социальные сети с использованием пакета Django REST framework OAuth. Подробно обо всем этом можно почитать на странице официальной документации:

https://www.django-rest-framework.org/api-guide/authentication/

Как видите, способов авторизации и аутентификации множество, а сторонних пакетов, которые их реализуют еще больше. Как во всем этом не потеряться? Для этого, очевидно, нужно хорошо понимать принципы работы различных алгоритмов авторизации, знать их сильные и слабые стороны, а, затем, при разработке конкретных проектов, выбирать наиболее подходящие варианты.

Авторизация на основе сессий (session-based)

Исторически, одним из первых способов авторизации и аутентификации пользователей основывается на cookies браузеров и сессиях сервера. Он широко используется и поныне. Идея алгоритма очень проста. Чтобы клиент был успешно идентифицирован на стороне сервера, он прежде должен войти в систему (полагаем, что он в ней уже зарегистрирован). Для этого сервер отправляет в браузер форму для ввода логина и пароля (опять же, как пример). Пользователь их вводит и отправляет POST-запрос с данными обратно на сервер. Сервер ищет в БД пользователя с указанными логином и паролем. Если не находит, то возвращает ошибку, например «Неверная пара логин/пароль». Если находит, то формирует идентификатор сессии в виде набора произвольных букв и цифр:

d3N2OnBhc3N3b3JkMTIz

заносит это значение в таблицу сессий, связывая session_id с пользователем (user_id). Затем, session id отправляется клиенту и браузер сохраняет его в своих cookies. Это процедура авторизации посредством сессий и cookies.

Если пользователь авторизован и в браузере хранится id сессии, то он может получать доступ к определенным, закрытым страницам сайта. Для этого, например, при GET-запросе, в заголовке прописывается строчка:

Authorization: Basic d3N2OnBhc3N3b3JkMTIz

в которой передается session id. Сервер ищет принятый id сессии с записями в БД и если находит такой session_id, то идентифицирует пользователя и разрешает ему доступ к закрытым страницам. Если же, по каким-либо причинам session_id в БД не находится, то авторизация не проходит и доступ остается закрытым.

Как правило, идентификатор сессии существует ограниченное время: от нескольких минут до нескольких месяцев. Реже – постоянно. Также session id меняется, если пользователь выйдет из системы. В этом случае запись удаляется из таблицы и повторная попытка зайти под этим же идентификатором уже будет безуспешной. Пользователю нужно будет снова войти в систему, будет назначен другой session id и доступ вновь будет открыт.

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

Реализация session-based аутентификации в DRF

Несмотря на эти недостатки, авторизация и аутентификация этим способом довольно распространена и встроена в Django REST Framework. Реализуется она очень просто. Достаточно в коллекции urlpatterns (в файле drfsite/urls.py) прописать строчку:

urlpatterns = [
    ...
    path('api/v1/drf-auth/', include('rest_framework.urls'))
]

Все. Запускаем тестовый веб-сервер, переходим по адресу:

http://127.0.0.1:8000/api/v1/drf-auth/

и нам здесь отображаются поддерживаемые маршруты, в частности:

  • api/v1/drf-auth/login/ - для входа в систему;
  • api/v1/drf-auth/logout/ - для выхода из системы.

Мало того, если выполнить любой API-запрос для DRF, например:

http://127.0.0.1:8000/api/v1/women/

то вверху на панели увидим ссылку для авторизации. Нажмем на нее, появится окно для ввода логина и пароля:

Мы можем войти здесь под администратором, так как DRF полностью интегрирован с фреймворком Django и все зарегистрированные на сайте пользователи автоматически могут использовать систему авторизации и аутентификации DRF.

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

Чтобы убедиться, что аутентификация пользователя производится по session id, можно открыть в браузере панель «Инспектор», перейти во вкладку «Network», обновить страницу и здесь (в самом верху выбрать страницу) можно посмотреть на содержимое переданного заголовка. В частности, мы видим следующую строчку для cookie:

csrftoken=CBwenFKcZc9uHFFqVDqWRyxJZYhUxBXinHFOUk4B2aOCC0I2HByf3KlUhy5WwTwv; sessionid=oyg9fmyh6t8g3wsk73ut4ayiagzid2a1

Здесь, во-первых, имеется csrftoken для защиты от взлома (подмены устройства, с которого осуществляется вход) и, во-вторых, sessionid, по которому выполняется аутентификация пользователя. То есть, при каждом запросе от клиента должна поступать эта информация, чтобы сервер мог корректно идентифицировать пользователя и разрешить ему вход в систему.

Я постарался предельно просто и доступно изложить идею авторизации на основе сессий и cookies, а также продемонстрировал, как можно подключить этот тип аутентификации пользователей в Django REST Framework. На следующем занятии мы продолжим эту тему и поговорим о более распространенном способе аутентификации для API-запросов через токены.

Курс по Django: https://stepik.org/a/183363