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, то должно заработать.

i
  • 11 листопада 2024 р. 04:15

Her home medication regimen included tamoxifen 20 mg daily priligy generika dapoxetine 60mg After consultation with specialists from the anesthesia and neurology departments, a diagnosis of trigeminal neuralgia was made

Коментарі

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

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

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

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

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

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
i
innorwall14 листопада 2024 р. 17:42
Як скопіювати файли в Linux If only females relatives with DZ offspring were considered these percentages were 23 order priligy online uk
i
innorwall14 листопада 2024 р. 15:09
Qt/C++ - Підручник 068. Hello World за допомогою системи збирання CMAKE в CLion ditropan pristiq dosing With the Yankees leading, 4 3, Rivera jogged in from the bullpen to a standing ovation as he prepared for his final appearance in Chicago buy priligy pakistan
i
innorwall14 листопада 2024 р. 10:05
EVILEG-CORE. Використання Google reCAPTCHA 2001; 98 29 34 priligy buy
i
innorwall14 листопада 2024 р. 10:00
PyQt5 - Урок 007. Працює з QML QtQuick (Сигнали та слоти) priligy 30mg Am J Obstet Gynecol 171 1488 505
Тепер обговоріть на форумі
i
innorwall14 листопада 2024 р. 09:39
добавить qlineseries в функции priligy amazon canada 93 GREB1 protein GREB1 AB011147 6
i
innorwall11 листопада 2024 р. 16:55
Всё ещё разбираюсь с кешем. priligy walgreens levitra dulcolax carbs The third ring was found to be made up of ultra relativistic electrons, which are also present in both the outer and inner rings
9
9Anonim25 жовтня 2024 р. 15:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…
ИМ
Игорь Максимов03 жовтня 2024 р. 10:05
Реализация навигации по разделам Спасибо Евгений!

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