На данный момент я активно работаю над приложение, которое будет работат с 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) }); }
Доброго времени.
А если кастомный юзер.
И активация юзера подтверждением по смс или звонку?
Добрый день
Если Вы имеетe ввиду, что модель пользователя наследована от AbstractUser и переопределена для проекта, то да - этот пример рабочий, я сейчас занимаюсь одним проектом, который будет иметь приложение и использую там кастомного пользователя.
А что касается подтверждения по СМС, то я участвовал несколько лет назад в проекте, где было подтверждение по СМС. Код показать не могу, но суть была в том, что там было два REST API View, сначало приложение стучалось на первый url, куда посылало логин и пароль, при получении ответа 200, приложение меняло форму ввода данных и ждало, пока пользователь заполнит код из СМС и подтвердит отправку. В этом случае отправка кода была уже на второй url, который проверял правильность кода, и уже в этом случае отдавал токен.
Естественно первый URL, который обрабатывал логин и пароль, стучался на API стороннего провайдера по отправке СМС, получал отправленный код и сохранял в отдельную таблицу, чтобы потом сравнить код и соответствие пользователя коду.
В моем случае кастом от AbstractBaseUser
Только токен я использую knox(Стандарт мобильной безопастности Samsung), потому и спросил
Дальше понятно:
Регистрация post на страницу урл api/register(можно сразу отправить запрос на звонок, после создания пользователя)
Если удачно то получим 4-x символьный код от провайдера, запищем его. Если неудачно то создаем свой и шлем смс
Cледом post-запрос на верификацию с кодом(при удаче - активируем пользователя, при не удаче 4 попытки на повтор)
В таком случае без напильника этот код сходу не взлетит, если конкретно knox нужно использовать.
А касательно самого AbstractBaseUser, то должно заработать.