Как реализовать аутентификацию по токену с помощью Django REST Framework

В этом руководстве вы узнаете, как реализовать аутентификацию на основе токенов с помощью Django REST Framework (DRF). Аутентификация на основе токенов работает путем обмена имени пользователя и пароля на токен, который будет использоваться во всех последующих запросах для идентификации пользователя на стороне сервера.

Специфика обработки аутентификации на стороне клиента зависит от технологии/языка/фреймворка, с которым вы работаете. Клиент может быть мобильным приложением, использующим iOS или Android. Это может быть настольное приложение с использованием Python или C++. Это может быть веб-приложение на PHP или Ruby.

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

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

Оглавление

Установка проекта REST API

Итак, давайте начнем с самого начала. Установите Django и DRF:

pip install django
pip install djangorestframework

Создайте новый проект Django:

django-admin.py startproject myapi .

Перейдите в папку myapi:

cd myapi

Запустите новое приложение. Я назову свое приложение core:

django-admin.py startapp core

Вот как должна выглядеть структура вашего проекта:

myapi/
 |-- core/
 |    |-- migrations/
 |    |-- __init__.py
 |    |-- admin.py
 |    |-- apps.py
 |    |-- models.py
 |    |-- tests.py
 |    +-- views.py
 |-- __init__.py
 |-- settings.py
 |-- urls.py
 +-- wsgi.py
manage.py

Добавьте приложение core (которое вы создали) и приложение rest_framework (которое вы установили) в INSTALLED_APPS, внутри модуля settings.py:

myapi/settings.py

INSTALLED_APPS = [
    # Django Apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-Party Apps
    'rest_framework',

    # Local Apps (Your project's apps)
    'myapi.core',
]

Вернитесь в корень проекта (в папку, где находится скрипт manage.py) и перенесите базу данных:

python manage.py migrate

Давайте создадим наше первое представление API просто для проверки:

myapi/core/views.py

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

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

Теперь зарегистрируйте путь в модуле urls.py:

myapi/urls.py

from django.urls import path
from myapi.core import views

urlpatterns = [
    path('hello/', views.HelloView.as_view(), name='hello'),
]

Итак, теперь у нас есть API с одной конечной точкой /hello/, к которой мы можем выполнять GET запросы. Мы можем использовать браузер для потребления этой конечной точки, просто обратившись к URL http://127.0.0.1:8000/hello/:

Hello Endpoint HTML

Мы также можем попросить получить ответ в виде простых данных JSON, передав параметр format в строке запроса, как http://127.0.0.1:8000/hello/?format=json:

Hello Endpoint JSON

Оба метода подходят для опробования DRF API, но иногда инструмент командной строки более удобен, так как мы можем легче играть с заголовками запросов. Вы можете использовать cURL, который широко доступен во всех основных дистрибутивах Linux/macOS:

curl http://127.0.0.1:8000/hello/

Hello Endpoint cURL

Но обычно я предпочитаю использовать HTTPie, который является довольно удивительным инструментом командной строки Python:

http http://127.0.0.1:8000/hello/

Hello Endpoint HTTPie

Теперь давайте защитим эту конечную точку API, чтобы мы могли реализовать аутентификацию с помощью токенов:

myapi/core/views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated  # <-- Here


class HelloView(APIView):
    permission_classes = (IsAuthenticated,)             # <-- And here

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

Попробуйте снова получить доступ к конечной точке API:

http http://127.0.0.1:8000/hello/

Hello Endpoint HTTPie Forbidden

Теперь мы получаем ошибку HTTP 403 Forbidden. Теперь давайте реализуем аутентификацию токена, чтобы мы могли получить доступ к этой конечной точке.

Внедрение аутентификации по токену

Нам нужно добавить две части информации в наш модуль settings.py. Сначала включите rest_framework.authtoken в ваш INSTALLED_APPS и включите TokenAuthentication в REST_FRAMEWORK:

myapi/settings.py

INSTALLED_APPS = [
    # Django Apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-Party Apps
    'rest_framework',
    'rest_framework.authtoken',  # <-- Here

    # Local Apps (Your project's apps)
    'myapi.core',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',  # <-- And here
    ],
}

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

python manage.py migrate

Migrate Auth Token

Теперь нам нужна учетная запись пользователя. Давайте просто создадим ее, используя утилиту командной строки manage.py:

python manage.py createsuperuser --username vitor --email vitor@example.com

Самый простой способ сгенерировать токен, только для целей тестирования, это снова использовать утилиту командной строки:

python manage.py drf_create_token vitor

drf_create_token

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

Но теперь, когда у нас есть TokenAuthentication на месте, давайте попробуем сделать еще один запрос к нашей конечной точке /hello/:

http http://127.0.0.1:8000/hello/

WWW-Authenticate Token

Обратите внимание, как наш API теперь предоставляет клиенту дополнительную информацию о требуемом методе аутентификации.

Наконец, давайте используем наш токен!

http http://127.0.0.1:8000/hello/ 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'

REST Token Authentication

И это практически все. Впредь во всех последующих запросах вы должны включать заголовок Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf.

Форматирование выглядит странно и обычно вызывает недоумение, как установить этот заголовок. Это зависит от клиента и от того, как установить заголовок запроса HTTP.

Например, если бы мы использовали cURL, то команда была бы примерно такой:

curl http://127.0.0.1:8000/hello/ -H 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'

Или если это был вызов Python requests:

import requests

url = 'http://127.0.0.1:8000/hello/'
headers = {'Authorization': 'Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'}
r = requests.get(url, headers=headers)

Однако, если бы мы использовали Angular, вы могли бы реализовать HttpInterceptor и установить заголовок:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const user = JSON.parse(localStorage.getItem('user'));
    if (user && user.token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Token ${user.accessToken}`
        }
      });
    }
    return next.handle(request);
  }
}

Пользователь запрашивает токен

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

Включите следующий маршрут в модуль urls.py:

myapi/urls.py

from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token  # <-- Here
from myapi.core import views

urlpatterns = [
    path('hello/', views.HelloView.as_view(), name='hello'),
    path('api-token-auth/', obtain_auth_token, name='api_token_auth'),  # <-- And here
]

Итак, теперь у нас есть совершенно новая конечная точка API, которая называется /api-token-auth/. Давайте сначала проверим его:

http http://127.0.0.1:8000/api-token-auth/

API Token Auth

Он не обрабатывает GET-запросы. По сути это просто представление для получения POST запроса с именем пользователя и паролем.

Попробуем еще раз:

http post http://127.0.0.1:8000/api-token-auth/ username=vitor password=123

API Token Auth POST

В теле ответа находится токен, связанный с этим конкретным пользователем. После этого момента вы храните этот токен и применяете его к будущим запросам.

Тогда, опять же, способ выполнения POST-запроса к API зависит от используемого языка/фреймворка.

Если бы это был клиент Angular, вы могли бы хранить токен в localStorage, если бы это было приложение Desktop CLI, вы могли бы хранить в текстовом файле в домашнем каталоге пользователя в файле dot.

Выводы

Надеемся, что это руководство дало некоторое представление о том, как работает аутентификация с помощью токенов. Я постараюсь продолжить это руководство, предоставив несколько конкретных примеров приложений Angular, приложений командной строки и веб-клиентов.

Важно отметить, что реализация Token по умолчанию имеет некоторые ограничения, такие как только один токен на пользователя, нет встроенного способа установить дату истечения срока действия токена.

Вернуться на верх