Russian (Pусский) translation by Anna k.Ivanova (you can also view the original English article)
В этой статье содержится введение в JSON Web Tokens (JWT) и руководство как реализовать JWT-аутентификацию в Django.
Что такое JWT?
JWT - это закодированная строка JSON, которая передается в заголовках для аутентификации запросов. Обычно она создается путем хэширования данных JSON с помощью секретного ключа. Это означает, что серверу не нужно каждый раз запрашивать базу данных, чтобы получить пользователя, связанного с данным токеном.
Как работают веб-маркеры JSON
Когда пользователь успешно выполняет вход в систему с использованием своих учетных данных, создается пользовательский токен JSON и сохраняется в локальном хранилище. Всякий раз, когда пользователь хочет получить доступ к защищенному URL-адресу, токен отправляется в заголовке запроса. Затем сервер проверяет правильность JWT в заголовке авторизации, и если он валидный, пользователю будет разрешен доступ.
Содержимое заголовка обычно выглядит так:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI
Ниже приведена диаграмма, показывающая этот процесс:

Концепция аутентификации и авторизации
Аутентификация - это процесс идентификации зарегистрированного пользователя, а авторизация - это процесс определения того, имеет ли определенный пользователь право доступа к веб-ресурсу.
Пример API
В этом учебном пособии мы собираемся создать простую систему аутентификации пользователей в Django, используя JWT в качестве механизма аутентификации.
Требования
- Джанго
- Python
Давайте начнем.
Создайте каталог, в котором вы будете содержать свой проект, а также виртуальную среду для установки зависимостей проекта.
mkdir myprojects cd myprojects virtual venv
Активируйте виртуальную среду:
source venv/bin/activate
Создайте проект Django.
django-admin startproject django_auth
Установите DRF и django-rest-framework-jwt, используя pip.
pip install djangorestframework pip install djangorestframework-jwt pip install django
Давайте продолжим и добавим DRF в список установленных приложений в файле settings.py
.
Настройка параметров JWT
Чтобы использовать JWT, нам нужно настроить разрешения django-rest-framework для принятия JSON Web Tokens.
В файле settings.py
добавьте следующие конфигурации:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ), }
Создайте новое приложение users, которое будет обрабатывать аутентификацию и управление пользователями.
cd django-auth django-admin.py startapp users
Добавьте приложение users в список установленных приложений в файле settings.py
.
Настройка базы данных
Мы собираемся использовать базу данных PostgreSQL, потому что она более стабильна и надежна.
Создайте базу данных auth
и назначьте пользователя.
Перейдите на учетную запись Postgres на вашем компьютере, набрав:
sudo su postgres
Зайдите в оболочку Postgres и создайте базу данных:
psql postgres=# CREATE DATABASE auth;
Создайте роль:
postgres=# CREATE ROLE django_auth WITH LOGIN PASSWORD 'asdfgh';
Предоставьте доступ к базе данных для пользователя:
postgres=# GRANT ALL PRIVILEGES ON DATABASE auth TO django_auth;
Установите пакет psycopg2, который позволит нам использовать настроенную нами базу данных:
pip install psycopg2
Отредактируйте текущую базу данных SQLite и используйте базу данных Postgres.
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'auth', 'USER': 'django_auth', 'PASSWORD': 'asdfgh', 'HOST': 'localhost', 'PORT': '', } }
Создание моделей
Django поставляется со встроенной системой аутентификации, которая очень сложна, но иногда нам нужно внести коррективы, и, следовательно, нам нужно создать собственную систему аутентификации пользователей. Наша модель пользователя будет наследоваться от класса AbstractBaseUser
, предоставляемого django.contrib.auth.models
.
В users/models.py мы начинаем с создания модели User для хранения деталей пользователя.
# users/models.py from __future__ import unicode_literals from django.db import models from django.utils import timezone from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin ) class User(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. """ email = models.EmailField(max_length=40, unique=True) first_name = models.CharField(max_length=30, blank=True) last_name = models.CharField(max_length=30, blank=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) date_joined = models.DateTimeField(default=timezone.now) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['first_name', 'last_name'] def save(self, *args, **kwargs): super(User, self).save(*args, **kwargs) return self
REQUIRED_FIELDS
содержит все обязательные поля в вашей модели пользователя, кроме поля имени пользователя и пароля, поскольку эти поля всегда будут запрашиваться.
UserManager
- это класс, определяющий методы create_user
и createuperuser
. Этот класс должен предшествовать классу AbstractBaseUser
, который мы определили выше. Давайте продолжим и определим это.
from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin, BaseUserManager ) class UserManager(BaseUserManager): def _create_user(self, email, password, **extra_fields): """ Creates and saves a User with the given email,and password. """ if not email: raise ValueError('The given email must be set') try: with transaction.atomic(): user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user except: raise def create_user(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(email, password, **extra_fields) def create_superuser(self, email, password, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self._create_user(email, password=password, **extra_fields)
Миграции
Миграции предоставляют способ обновления вашей схемы базы данных каждый раз, когда ваши модели меняются, не теряя при этом данные.
Создайте первоначальную миграцию для модели наших пользователей и выполните синхронизацию базы данных.
python manage.py make migrations users python manage.py migrate
Создание суперпользователя
Создайте суперпользователя, выполнив следующую команду:
python manage.py createsuperuser
Создание новых пользователей
Давайте создадим ендпоинт, чтобы разрешить регистрацию новых пользователей. Мы начнем с сериализации полей модели пользователя. Сериализаторы предоставляют способ изменения данных в форме, которую легче понять, например, JSON или XML. Десериализация делает обратное, преобразуя данные в форму, которая может быть сохранена в базе данных.
Создайте users/serializers.py и добавьте следующий код.
# users/serializers.py from rest_framework import serializers from.models import User class UserSerializer(serializers.ModelSerializer): date_joined = serializers.ReadOnlyField() class Meta(object): model = User fields = ('id', 'email', 'first_name', 'last_name', 'date_joined', 'password') extra_kwargs = {'password': {'write_only': True}}
CreateUserAPIView
Затем мы хотим создать представление, чтобы у клиента был URL-адрес для создания новых пользователей.
В user.views.py добавьте следующее:
# users/views.py class CreateUserAPIView(APIView): # Allow any user (authenticated or not) to access this url permission_classes = (AllowAny,) def post(self, request): user = request.data serializer = UserSerializer(data=user) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED)
Мы устанавливаем allow_classes
в (AllowAny,)
, чтобы разрешить любому пользователю (прошедшему проверку подлинности или нет) доступ к этому URL-адресу.
Настройка URL-адресов
Создайте файл users/urls.py
и добавьте URL-адрес в соответствие с созданным нами представлением. Также добавьте следующий код.
# users/urls.py from django.conf.urls import url, patterns from .views import CreateUserAPIView urlpatterns = [ url(r'^create/$', CreateUserAPIView.as_view()), ]
Нам также нужно импортировать URL-адреса из приложения-пользователя в основной файл django_auth/urls.py.
Так что делаем это и двигаемся дальше. Мы используем здесь функцию include
, поэтому не забудьте ее импортировать.
# django_auth/urls.py from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', include('users.urls', namespace='users')), ]
Теперь, когда мы закончили создание ендпоинта, давайте протестируем его. Для тестов мы будем использовать Postman. Если вы не знакомы с Postman, это инструмент, который представляет дружественный графический интерфейс для построения запросов и чтения ответов.

Как вы можете видеть выше, ендпоинт работает так, как ожидалось.
Аутентификация пользователей
Мы будем использовать модуль Django-REST Framework JWT Python, который мы установили в начале этого руководства. Он добавляет поддержку JWT-аутентификации для приложений Django Rest Framework.
Но сначала давайте определим некоторые параметры конфигурации для наших токенов и как они сгенерированы в файле settings.py.
# settings.py import datetime JWT_AUTH = { 'JWT_VERIFY': True, 'JWT_VERIFY_EXPIRATION': True, 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3000), 'JWT_AUTH_HEADER_PREFIX': 'Bearer', }
-
JWT_VERIFY
: он вызовет jwt.DecodeError, если секрет неправильный. -
JWT_VERIFY_EXPIRATION
: Устанавливает истечение срока действия в True, то есть токены истекают через некоторое время. Время по умолчанию - пять минут. -
JWT_AUTH_HEADER_PREFIX
: Префикс значения заголовка авторизации, который требуется отправить вместе с токеном. Мы установили его какBearer
, а по умолчанию -JWT
.
В users/views.py
добавьте следующий код.
@api_view(['POST']) @permission_classes([AllowAny, ]) def authenticate_user(request): try: email = request.data['email'] password = request.data['password'] user = User.objects.get(email=email, password=password) if user: try: payload = jwt_payload_handler(user) token = jwt.encode(payload, settings.SECRET_KEY) user_details = {} user_details['name'] = "%s %s" % ( user.first_name, user.last_name) user_details['token'] = token user_logged_in.send(sender=user.__class__, request=request, user=user) return Response(user_details, status=status.HTTP_200_OK) except Exception as e: raise e else: res = { 'error': 'can not authenticate with the given credentials or the account has been deactivated'} return Response(res, status=status.HTTP_403_FORBIDDEN) except KeyError: res = {'error': 'please provide a email and a password'} return Response(res)
В приведенном выше коде в окне входа в систему вводится имя пользователя и пароль, а затем создается токен с информацией пользователя, соответствующей переданным учетным данным, в качестве пейлоада и возвращается в браузер. Другие данные пользователя, такие как имя, также возвращаются в браузер вместе с токеном. Этот токен будет использоваться для аутентификации в будущих запросах.
Классы разрешений устанавливаются в allowAny
, поскольку любой пользователь может получить доступ к этому ендпоинту.
Мы также сохраняем последнее время входа пользователя с этим кодом.
user_logged_in.send(sender=user.__class__, request=request, user=user)
Каждый раз, когда пользователь хочет сделать запрос API, он должен отправить токен в заголовках Auth, чтобы аутентифицировать запрос.
Давайте протестируем этот ендпоинт с помощью Postman. Откройте Postman и используйте запрос для аутентификации с одним из пользователей, которых вы создали ранее. Если попытка входа в систему выполнена успешно, ответ будет выглядеть следующим образом:
.png)
Получение и обновление пользователей
Пока пользователи могут зарегистрироваться и пройти проверку подлинности. Тем не менее, так же нужно предоставить способ получения и обновления этой информации. Давайте реализуем это.
В файле user.views.py
добавьте следующий код.
class UserRetrieveUpdateAPIView(RetrieveUpdateAPIView): # Allow only authenticated users to access this url permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get(self, request, *args, **kwargs): # serializer to handle turning our `User` object into something that # can be JSONified and sent to the client. serializer = self.serializer_class(request.user) return Response(serializer.data, status=status.HTTP_200_OK) def put(self, request, *args, **kwargs): serializer_data = request.data.get('user', {}) serializer = UserSerializer( request.user, data=serializer_data, partial=True ) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data, status=status.HTTP_200_OK)
Сначала мы определяем классы разрешений и устанавливаем IsAuthenticated
, так как это защищенный URL-адрес, и к нему могут обращаться только прошедшие проверку подлинности пользователи.
Затем мы определяем метод get
для получения сведений о пользователе. После получения сведений о пользователе аутентифицированный пользователь затем по желанию обновит свои данные.
Обновите URL-адреса, чтобы определить ендпоинт следующим образом.
users/urls.py from .views import CreateUserAPIView, UserRetrieveUpdateAPIView urlpatterns = [ url(r'^update/$', UserRetrieveUpdateAPIView.as_view()), ]
Чтобы запрос был успешным, заголовки должны содержать JWT токен, как показано ниже.

Если вы попытаетесь запросить ресурс без заголовка проверки подлинности, вы получите следующую ошибку.

Если пользователь не делает запросов дольше времени, указанного в JWT_EXPIRATION_DELTA
, то токен истекает, и ему придется запросить другой токен. Это также показано ниже.

Заключение
В этом руководстве описано, что необходимо для успешного создания надежной системы аутентификации с помощью JSON Web Tokens.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post