Как реализовать аутентификацию через токен в Django Rest Framework


11 июля 2022


Настраиваем токен аутентификацию с Django Rest Framework

Django Rest Framework (drf) поддерживает простую аутентификацию с помощью токена. Такой тип аутентификации чаще всего используется для доступа к API. В этой статье будет показано как использовать такой тип аутентификации в вашем API.

 

 

Где используется токен-аутентификация

HTTP является 'stateless' протоколом (не хранящий состояния). Это значит, что, каждый запрос для сервера будет новым, хоть вы уже обращались к нему десяток раз. Из-за этого, в заголовке каждого запроса, содержится множество информации, в том числе данные аутентификации.

Аутентификация может работать по разному. Ниже часть популярных реализаций, которые поддерживает Django Rest Framework:

  • BasicAuthentication
  • TokenAuthentication
  • SessionAuthentication
  • RemoteUserAuthentication

Преимущество TokenAuthentication и SessionAuthentication над BasicAuthentication в том, что вы передаете пару логин и пароль единожды, а затем можете использовать уникальные идентификаторы. В случае сессий идентификаторы помещаются в заголовок Cookies, а у токенов - сгенерированная строка, например "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", в заголовке Authorization. 

Аутентификация на основе сессий может создать сложности в некоторых случаях, например:

  • Вам нужно использовать одну сессию в нескольких доменах. Во многих случаях это будет невозможно сделать;
  • Если у вас несколько клиентов в т.ч. настольные и мобильные приложения. Сессии могут хранить не только данные сессии, но и об приложении. Они могут быть не нужны и с ними будет сложнее работать;
  • Токены являются лучшим способом защиты от CSRF атак.

Сам фреймворк drf может одновременно работать с несколькими типами аутентификации. Т.е. использовать сессию для браузера и токен для мобильного приложения.

Различия обычной токен аутентификации от JWT и OAuth

Со словами токен аутентификации так же часто появляются понятия JWT (JSON Web Token) и OAuth 2. Это может создать путаницу. Различие можно понять, если представить строку ''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" как токен. Роль этого токена в разных случаях:

  • Обычная токен аутентификация - это значение в базе данных, которое связано с пользователем и проверяется в каждом запросе;
  • В случае JWT - это закодированные и подписанные данные, которые можно раскодировать и получить JSON строку идентифицирующую пользователя и срок работы токена. Преимущество в том, что запрос к базе данных не делается;
  • OAuth 2 - является протоколом авторизации, а не аутентификации. Он определяет какие права будут у другого приложения для доступа к ресурсам пользователя. Отдельно определяется сам токен, который может быть, например, JWT.

Эти возможности реализуются через сторонние библиотеки. JWT, например, можно создать через Dj-Rest-Auth (раннее Django-rest-auth), а OAuth через djangorestframework-oauth.

 

 

Создание тестового приложения

Создадим простое приложение, на котором будет показан пример использования аутентификации с токеном. Установим необходимые пакеты:

pip install django djangorestframework

Создадим проект и приложение внутри него:

django-admin startproject api
cd api
python manage.py startapp app

В файле настроек проекта добавим установленный фреймворк 'djangorestframework' и созданное приложение 'app' в INSTALLED_APPS:

# api/setting.py

INSTALLED_APPS = [
    ....
    # Установленные пакеты
    'rest_framework',

    # Созданное приложение
    'app.apps.AppConfig',
]

Обновление INSTALLED_APPD для Django Rest Framework

Создадим вьюшку, которая будет возвращать какое-то сообщение:

# app/views.py

from rest_framework.views import APIView
from rest_framework.response import Response


class Index(APIView):

    def get(self, request):
        content = {'message': 'Hello, World!'}
        return Response(content)

Создание простого приложения с возвратом сообщения в Django Rest Framework

Свяжем этот класс с URL:

# api/urls.py

from django.urls import path
from app import views

urlpatterns = [
    path('', views.Index.as_view()),
]

Объявление маршрута для Django Rest Framework

Выполним миграцию и запустим сервер:

python manage.py migrate
python manage.py runserver

Выполнение миграций и запуск сервера для Django

В Django Rest Framework предусмотрены заранее готовые разрешения, которые мы можем использовать. Их достаточно много. Мы будем использовать только "IsAuthenticated" проверяющий, что пользователь прошел аутентификацию предоставив верные данные:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated 


class Index(APIView):
    permission_classes = (IsAuthenticated,) 

    def get(self, request):
        content = {'message': 'Hello, World!'}
        return Response(content)

Теперь, при открытии страницы, будет появляться сообщение ''Authentication credentials were not provided." и 403 ошибку так как мы не аутентифицированы. Так же может быть 401 ошибка, если у вас будет возвращаться заголовок с "WWW-Authenticate" (может зависеть от установленных классов аутентификации).

Ошибка с аутентификацией в Django Rest Framework

 

Настройка аутентификации

Токен-аутентификация работает через отдельное приложение, которое нужно подключить в "INSTALLED_APPS". Это приложение устанавливается вместе с django rest framework:

# api/settings.py

INSTALLED_APPS = [
    ....
    # Установленные пакеты
    'rest_framework',
    'rest_framework.authtoken',

    # Созданное приложение
    'app.apps.AppConfig'
]

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

# api/settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
}

Объявление общего типа аутентификации в Django Rest Framework

Подобная настройка говорит, что если будет требоваться аутентификация для drf приложений, она будет работать только через токен. Вы можете добавить и другие классы. Например с 'rest_framework.authentication.SessionAuthentication' вы сможете использовать ваши drf приложения через браузер.

После этого повторно выполнить миграцию:

python manage.py migrate

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

....
from rest_framework.authentication import SessionAuthentication

class Index(APIView):
    permission_classes = (IsAuthenticated,)
    authentication_classes = (SessionAuthentication,)
...

Создание токена

Токен выдается какому-то пользователю. Если у вас нет пользователя, то можете его создать:

python manage.py createsuperuser --email test@test.ru --username test

Один из способов отправки токена - это 'manage.py'. В примере ниже мы выдадим токен пользователю 'test':

python manage.py drf_create_token test

Создание токена через manage.py в Django

Кроме получения токена через 'manage.py' это можно сделать по url. Для этого есть отдельная вьюшка 'obtain_auth_token', которую нужно указать в 'urls.py':

# api/urls.py

from django.urls import path
from app import views
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    path('', views.Index.as_view()),
    path('token/', obtain_auth_token),
]

После этого мы можем обращаться по url 'http://127.0.0.1/token' для получения токена. Пример с requests:

import requests

url = 'http://127.0.0.1:8000/token/'

r = requests.post(url, data={
  'username': 'test',
  'password': 'test'
  }
)

Выполнение запроса для получение токена в Django Rest Framework

Процесс получения токена достаточно простой. В случае вьюшки 'obtain_auth_token' - это наследование с APIView, использование сериализатора, со сверкой логина и пароля, и модели 'Token', которая генерирует и сохраняет токен. Сама таблица с токеном выглядит так:

Таблица с токенами в Django Rest Framework

Пример создания токена через shell и модель.

Создание токена в Django через обращение к модели

Отправка запросов с токеном

Что бы передать токен его нужно поместить в заголовок 'Authorization'. Пример через requests:

import requests

url = 'http://127.0.0.1:8000/'

r = requests.get(url, headers={
  'Authorization': 'Token 8072f04f9a1eacbd52fcf7d8289a52245f2454eb'
  }
)

Или в Powershell и Curl:

Invoke-RestMethod 'http://127.0.0.1:8000' -Headers @{'Authorization'='Token 8072f04f9a1eacbd52fcf7d8289a52245f2454eb'}

curl -H "Authorization: Token 8072f04f9a1eacbd52fcf7d8289a52245f2454eb" http://127.0.0.1:8000

Отправка токена через Powershell

...

Теги: #python #django #rest


Каналы
Telegram FixMyPc Telegram Лента FixMyPC RSS Rss
Популярные тэги
О блоге
Этот блог представляет собой конспекты выученного материала, приобретённого опыта и лучшие практики в системном администрировании и программировании.