Курс по Django: https://stepik.org/a/183363
Пришло время
познакомиться с процессом авторизации и аутентификации пользователей
посредством токенов. Базовая идея, лежащая во всех подобных алгоритмах,
использование определенного ключа (токена) для идентификации пользователя.
Причем токен не привязан ни к домену, ни к браузеру, главное указать его в
заголовке запроса, а откуда он был отправлен – неважно. Благодаря этому сайт
легко может взаимодействовать с самыми разными конечными устройствами при
аутентификации пользователей.
В Django REST Framework популярны два
подхода при реализации токенов:
-
стандартная
аутентификация токенами (библиотека Djoser);
-
JWT-токены
(библиотека Simple JWT).
но первый подход
используется заметно чаще.
По идее, обычные
токены в DRF можно
реализовать напрямую, без установки каких-либо дополнительных библиотек. В
официальной документации:
https://www.django-rest-framework.org/api-guide/authentication
подробно
объясняется, как это сделать. Но, при разработке реальных проектов, очень часто
применяют библиотеку Djoser, если требуется реализовать
авторизацию и аутентификацию пользователей посредством обычных токенов. Эта
библиотека содержит довольно богатый функционал и может быть адаптирована под
каждую конкретную задачу. А потому нет смысла детально реализовывать
функционал, который уже представлен в виде пакета Djoser. Хотя, я
рекомендую первый раз сделать токены вручную, опираясь на документацию DRF. Пусть это
будет вашим домашним заданием. Ну а мы в рамках этого занятия познакомимся с механизмом
авторизации и аутентификации с помощью обычных токенов с использованием Djoser.
Идея аутентификации по токенам
Вначале разберемся
с порядком авторизации и аутентификации пользователей по токенам. Предположим,
пользователь зарегистрирован на некотором сайте и вводит в форму логин и
пароль. Данные отправляются обратно на сервер POST-запросом и
сверяются с БД. Если указанная пара логин/пароль присутствуют в таблице БД, то
сервер возвращает пользователю некую комбинацию из букв и цифр, которая и
является токеном. Кроме того, данный токен заносится в БД на сервере и
сохраняется в любом защищенном локальном хранилище на устройстве.
В дальнейшем для
аутентификации пользователя в каждом заголовке запроса от него к серверу
прописывается специальная строчка, например:
Authorization:
Token 401f7ac837da42b97f613d789819ff93537bee6a
Сервер читает
заголовок запроса, находит запись с токеном, сверяет его по своей БД и если
находит, то пользователь считается авторизованным.
Обратите
внимание, время жизни токена, по умолчанию, достаточно большое (разумеется,
пока пользователь не выйдет из системы и токен не будет удален из таблицы). То
есть, единожды полученный токен может быть многократно использован самыми
разными устройствами для доступа к приватной информации.
Реализация токенов с помощью пакета Djoser
Давайте теперь
реализуем аутентификацию по токенам с помощью пакета Djoser. Подробную
информацию по этому пакету можно посмотреть на официальном сайте:
https://djoser.readthedocs.io/en/latest/
Вначале нам его
нужно установить. Для этого в терминале выполним команду:
pip install djoser
и начнется
установка пакета со всеми необходимыми зависимостями.
Далее, подключим
эту библиотеку к нашему проекту. Перейдем в drfsite/settings.py и в коллекцию INSTALLED_APPS
добавим следующие строчки:
INSTALLED_APPS = [
...
'rest_framework.authtoken',
'djoser',
]
Первая строчка –
это подключение стандартной модели таблицы для поддержки токенов, а вторая –
непосредственно пакет Djoser.
Так как у нас
появилась новая модель, то нужно выполнить миграции для создания таблицы
непосредственно в БД:
python manage.py migrate
Все, приложение
подключено и таблицы созданы. Осталось связать Djoser с конкретными
маршрутами. Откроем документацию:
https://djoser.readthedocs.io/en/latest/authentication_backends.html
Здесь указаны
пути, которые нужно прописать в файле drfsite/urls.py в коллекции urlpatterns:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/women/', WomenAPIList.as_view()),
path('api/v1/women/<int:pk>/', WomenAPIUpdate.as_view()),
path('api/v1/womendelete/<int:pk>/', WomenAPIDestroy.as_view()),
path('api/v1/drf-auth/', include('rest_framework.urls')),
path('api/v1/auth/', include('djoser.urls')), # new
re_path(r'^auth/', include('djoser.urls.authtoken')), # new
]
Также в файле drfsite/settings.py необходимо
разрешить авторизацию по токенам:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
Теперь у нас все
готово и мы можем протестировать работу приложения. Запустим тестовый
веб-сервер:
python manage.py runserver
Первым делом
попробуем создать нового пользователя. Да, библиотека Djoser позволяет
добавлять, удалять, изменять данные по пользователям. Все это подробно описано
в разделе «Base Endpoints» официальной документации:
https://djoser.readthedocs.io/en/latest/base_endpoints.html
Мы здесь видим,
что для регистрации нового пользователя нужно обязательно по POST-запросу
передать имя пользователя и пароль по адресу:
http://127.0.0.1:8000/api/v1/auth/users/
Сделаем это с
помощью программы Postman, которая будет имитировать запрос от
некоего устройства, например, смартфона. Вводим указанный адрес, выбираем тип
запроса POST и во вкладке «Body» отмечаем пункт
«form-data» и вводим:
username: seconduser
password: fghgfhhuser123
email: seconduser@mail.ru
Нажимаем кнопку «Send» и видим, что
новый пользователь был успешно добавлен. Аналогично можно делать и остальные
действия с пользователями, приведенные в документации.
Давайте теперь
авторизуемся с помощью этого нового пользователя. Перейдем в раздел «Token
Endpoints» документации:
https://djoser.readthedocs.io/en/latest/token_endpoints.html
и видим, что для
авторизации нужно использовать маршрут:
http://127.0.0.1:8000/auth/token/login/
Откроем в Postman новую вкладку,
введем этот URL-адрес, выберем
метод POST, перейдем во
вкладку «Body», отметим «form-data» и укажем для
ввода:
username: seconduser
password: fghgfhhuser123
После отправки
запроса нам возвращается ответ в виде JSON-строки с
присвоенным токеном для последующей аутентификации:
{"auth_token":"ff8e194d179c939842a90c2d725ce5f40da0ec36"}
Давайте
проверим, как работает аутентификация через токены. Для этого мы в классе WomenAPIUpdate разрешим
получать доступ к отдельной записи только авторизованным пользователям:
class WomenAPIUpdate(generics.RetrieveUpdateAPIView):
queryset = Women.objects.all()
serializer_class = WomenSerializer
permission_classes = (IsAuthenticated, )
Затем, перейдем в программу Postman и во вкладке укажем адрес:
http://127.0.0.1:8000/api/v1/women/9/
Если сейчас
выполнить GET-запрос, то
получим ответ:
{"detail":"Учетные
данные не были предоставлены."}
Чтобы сервер
принял нас, как авторизованного пользователя, в заголовке запроса нужно
прописать строку:
Authorization:
Token ff8e194d179c939842a90c2d725ce5f40da0ec36
Теперь, при
отправке запроса мы получаем данные по указанной записи. То есть, указав в GET-запросе
выданный нам токен, мы успешно проходим процедуру аутентификации на сервере и
получаем доступ к закрытой информации.
Наконец, чтобы
выйти из системы, нам нужно отправить POST-запрос на
адрес:
http://127.0.0.1:8000/auth/token/logout/
Для этого в Postman на новой
вкладке укажем этот URL, выберем метод POST и если сейчас
отправить его, то вернется строка, что пользователь не авторизован. Поэтому
здесь нам также нужно в заголовке запроса указать:
Authorization:
Token ff8e194d179c939842a90c2d725ce5f40da0ec36
Теперь, при
повторной отправке мы видим значение 1, то есть, токен был успешно удален из БД
и теперь он недействителен. Это легко можно проверить, если отправить запрос на
адрес:
http://127.0.0.1:8000/api/v1/women/9/
Получим ответ от
сервера:
{"detail":"Недопустимый
токен."}
то есть, мы
действительно вышли из системы.
Сейчас в нашем
проекте пользователи могут использовать две независимые системы авторизации:
через сессии и токены. Однако, на уровне каждого отдельного класса
представления мы можем конкретизировать способ аутентификации пользователя.
Например, если в классе WomenAPIUpdate прописать
атрибут authentication_classes с классом TokenAuthentication, то данные по
записи можно получать только при авторизации по токенам:
class WomenAPIUpdate(generics.RetrieveUpdateAPIView):
queryset = Women.objects.all()
serializer_class = WomenSerializer
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
Перейдем в
браузере на страницу:
http://127.0.0.1:8000/api/v1/women/9/
и видим, что
данные не отображаются. Даже если мы авторизуемся через механизм сессий, то все
равно данные не будут отображены. А вот через токены мы имеем к ним доступ. Вот
так тонко на уровне отдельных представлений можно управлять способом
атуентификации пользователей.
Я, думаю, из
этого занятия вы поняли основной принцип реализации и взаимодействия с учетными
записями пользователей через токены с использованием пакета Djoser. На следующем
занятии мы продолжим эту тему и поговорим о JWT-токенах.
Курс по Django: https://stepik.org/a/183363