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',
]
Создадим вьюшку, которая будет возвращать какое-то сообщение:
# 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)
Свяжем этот класс с URL:
# api/urls.py
from django.urls import path
from app import views
urlpatterns = [
path('', views.Index.as_view()),
]
Выполним миграцию и запустим сервер:
python manage.py migrate
python manage.py runserver
В 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" (может зависеть от установленных классов аутентификации).
Настройка аутентификации
Токен-аутентификация работает через отдельное приложение, которое нужно подключить в "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',
],
}
Подобная настройка говорит, что если будет требоваться аутентификация для 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' это можно сделать по 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'
}
)
Процесс получения токена достаточно простой. В случае вьюшки 'obtain_auth_token' - это наследование с APIView, использование сериализатора, со сверкой логина и пароля, и модели 'Token', которая генерирует и сохраняет токен. Сама таблица с токеном выглядит так:
Пример создания токена через shell и модель.
Отправка запросов с токеном
Что бы передать токен его нужно поместить в заголовок '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
...