Evgenii Legotckoi
Evgenii Legotckoi14 лютого 2022 р. 04:24

Django Rest Framework - Урок 001. Додавання аутентифікації за токеном

На даний момент я активно працюю над програмою, яка буде працювати з REST API сайту на Django. І одним з перших кроків було налаштування аутентифікації користувача за токеном, але для того, щоб це запрацювало, потрібно спочатку отримати токен авторизації.

Розгляньмо, як це можна зробити.


Установка Django Rest Framework

Оскільки це найперша стаття із застосування DRF, то опишу докладніший процес встановлення та налаштування DRF. У Подальшому я вже не наводитиму цих інструкцій.

Встановлюємо DRF та інші необхідні пакети

pip install djangorestframework
pip install markdown       # Markdown support for the browsable API.
pip install django-filter

Далі додаємо програму 'rest_frameword' в INSTALLED_APPS у файлі конфігурації сайту settings.py

INSTALLED_APPS = [
    ...
    'rest_framework',
]

Далі офіційна документація пропонує підключити в urls.py файлі login та logout View із rest_framework, щоб можна було зручно користуватися API у браузері.

Якщо чесно, то я не використовую це API, оскільки мені зручніше користуватися документацією, що згенерована через swagger, про що я теж скажу далі, але я все одно підключаю дані View, щоб потім не вирішувати випадкові проблеми.

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

Далі налаштовуємо класи автентифікації та прав доступу за замовчуванням (також у settingsp.py)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

Тепер все готове, щоб написати точку доступу для отримання токена. Але попередньо я хочу підключити ще й генерування swagger документації. Просто так зручніше працювати, мені, наприклад, дуже подобається документація redoc, в якій досить красиво і зручно все відображається.

Установка Swagger

А зараз одразу підключимо swagger на сайті. Я розумію, що це не відноситься до теми статті, але в коді я роблю спеціальні маніпуляції, щоб підключити спеціальне відображення документації і вважаю, що надалі це може стати Вам у нагоді, оскільки:

  • Ви будете надалі використовувати swagger
  • Вам доведеться іноді кастомізувати виведення інформації в описі API, а саме для токена у мене була найспецифічніша зміна для swagger.

Установим drf-yasg

Для цього керуючись документацією виконаємо наступну команду

pip install -U drf-yasg

Після цього модифікуємо файл settings.py

INSTALLED_APPS = [
   ...
   'django.contrib.staticfiles',  # required for serving swagger ui's css/js files
   'drf_yasg',
   ...
]

А тепер зареєструємо swagger в urls сайту

from django.urls import re_path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions

schema_view = get_schema_view(
   openapi.Info(
      title="EXAMPLE API",
      default_version='v1',
      description="REST API Documentation",
      contact=openapi.Contact(email="example@example.com"),
   ),
   public=True,
   permission_classes=(permissions.IsAdminUser,),
)

urlpatterns = [
    ...
    re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
    re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]

Якщо ви уважно подивіться на код, побачите, що я зареєстрував swagger тільки для адміністрації сайту. Якщо хочете, щоб документація була відкритою, видаліть цей рядок

permission_classes=(permissions.IsAdminUser,),

Додатково можна додати інформацію у виведення документації про те, які типи автентифікації використовуються для API. Це виконується у файлі settings.py.

SWAGGER_SETTINGS = {
   'SECURITY_DEFINITIONS': {
      'Basic': {
            'type': 'basic'
      },
      'Bearer': {
            'type': 'apiKey',
            'name': 'Authorization',
            'in': 'header'
      }
   }
}

Ось тепер можна приступити до реалізації точки доступу для отримання токена.

Додаток API

Створимо програму api, в якій буде додано кінцеву точку для отримання токена.

python manage.py startapp api

serializers.py

Далі створимо файл із серіалізаторами, в якому буде створено спеціальний серіалізатор, який описуватиме відповідь сайту в swagger.

# -*- coding: utf-8 -*-

from rest_framework import serializers


class Response201AuthTokenSerializer(serializers.Serializer):
    token = serializers.CharField(required=True, allow_blank=False)
    user_id = serializers.IntegerField(required=True)

Цей серіалізатор коректно генеруватиме інформацію про відповідь у документації, і я її використовую для створення JSON відповіді з токеном.

views.py

Після чого модифікує файл views.py, щоб отримати кінцеву точку.

# -*- coding: utf-8 -*-

from django.contrib.auth import get_user_model
from django.http import JsonResponse
from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import authentication, viewsets
from rest_framework.authtoken.views import ObtainAuthToken

from api.serializers import Response201AuthTokenSerializer


class CustomAuthToken(ObtainAuthToken):
    authentication_classes = [authentication.BasicAuthentication]

    @swagger_auto_schema(responses={
        "201": openapi.Response(
            description=_("User has got Token"),
            schema=Response201AuthTokenSerializer,
        )
    })
    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data,
                                           context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        response201 = Response201AuthTokenSerializer(data={"token": token.key, "user_id": user.pk})
        response201.is_valid(raise_exception=True)
        return JsonResponse(response201.data)

Як бачите в коді є кастомізована схема swagger відповіді 201, контент створений. З серіалізатора Response201AuthTokenSerializer буде сформовано коректну документацію.
Крім іншого я використовую Response201AuthTokenSerializer, щоб сфомувати відповідь з токеном, а також id користувача. Надалі я використовую ID користувача для маніпуляції з даними в мобільному додатку.
Відмінним нюансом даного view є те, що як аутентифікація використовується лише BasicAuthenttication, тобто користувач отримує доступ до даного view тільки через username та password. Надалі розробка API ведеться від використання токена.

urls.py

А тепер підключити view для отримання токена у файлах urls

# -*- coding: utf-8 -*-

from django.urls import path

from api import views


app_name = 'api'
urlpatterns = [
    path('api-token-auth/', views.CustomAuthToken.as_view()),
]

У цьому файлі визначаємо ім'я програми, що використовується для формування маршрутів, тобто api. Також підключаємо view токена до маршрутизації.

І в кінці підключаємо urls.py програми api в основному файлі urls.py всього проекту.

urlpatterns = [
    ...
    path('api/', include('api.urls')),
    ...
]

Тепер, щоб отримати токен, необхідно направити POST запит по URL http://127.0.0.1/api/api-token-auth/**, в тому випадку, щоб тестувати функціонал на локальному сервері. У разі бойового сервера потрібно надсилати запит на адресу з доменом вашого сайту.

Для аутентифікації необхідно використовувати Basic аутентифікацію.

Приклад запиту маркера в Felgo QML

Ось приклад коду для запиту токена у додатку написаному на QML Felgo

function login(username, password, success, error) {
    HttpRequest
    .post("http://127.0.0.1/api/api-token-auth/", {username: username, password: password})
    .timeout(5000)
    .set('Content-Type', 'application/json')
    .then(function(res) { success(res) })
    .catch(function(err) { error(err) });
}
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

BL4CK R4BBIT
  • 14 лютого 2022 р. 06:42

Доброго времени.
А если кастомный юзер.
И активация юзера подтверждением по смс или звонку?

Evgenii Legotckoi
  • 14 лютого 2022 р. 06:54
  • (відредаговано)

Добрый день

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

А что касается подтверждения по СМС, то я участвовал несколько лет назад в проекте, где было подтверждение по СМС. Код показать не могу, но суть была в том, что там было два REST API View, сначало приложение стучалось на первый url, куда посылало логин и пароль, при получении ответа 200, приложение меняло форму ввода данных и ждало, пока пользователь заполнит код из СМС и подтвердит отправку. В этом случае отправка кода была уже на второй url, который проверял правильность кода, и уже в этом случае отдавал токен.

Естественно первый URL, который обрабатывал логин и пароль, стучался на API стороннего провайдера по отправке СМС, получал отправленный код и сохранял в отдельную таблицу, чтобы потом сравнить код и соответствие пользователя коду.

BL4CK R4BBIT
  • 14 лютого 2022 р. 07:14
  • (відредаговано)

В моем случае кастом от AbstractBaseUser

class User(AbstractBaseUser):
    phone       = PhoneNumberField(region='RU', unique=True)
    username    = models.CharField(max_length = 150, blank = True, null = True)
    standard    = models.CharField(max_length = 3, blank = True, null = True)
    score       = models.IntegerField(default = 16)
    first_login = models.BooleanField(default=False)
    active      = models.BooleanField(default=False)
    staff       = models.BooleanField(default=False)
    admin       = models.BooleanField(default=False)
    timestamp   = models.DateTimeField(auto_now_add=True)

    USERNAME_FIELD = 'phone'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def __str__(self):
        return self.phone

    def get_full_name(self):
        return self.phone

    def get_short_name(self):
        return self.phone

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):

        return True

    @property
    def is_staff(self):
        return self.staff

    @property
    def is_admin(self):
        return self.admin

    @property
    def is_active(self):
        return self.active

Только токен я использую knox(Стандарт мобильной безопастности Samsung), потому и спросил

Дальше понятно:
Регистрация post на страницу урл api/register(можно сразу отправить запрос на звонок, после создания пользователя)
Если удачно то получим 4-x символьный код от провайдера, запищем его. Если неудачно то создаем свой и шлем смс
Cледом post-запрос на верификацию с кодом(при удаче - активируем пользователя, при не удаче 4 попытки на повтор)

Evgenii Legotckoi
  • 14 лютого 2022 р. 07:22

В таком случае без напильника этот код сходу не взлетит, если конкретно knox нужно использовать.
А касательно самого AbstractBaseUser, то должно заработать.

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах