Из этого туториала вы узнаете, как использовать JSON Web Tokens (JWT) и как реализовать аутентификацию JWT в Django.
Что такое JWT?
JWT — это закодированная строка JSON, которая передается в заголовках для проверки подлинности запросов. Обычно его получают путем хеширования данных JSON с помощью секретного ключа. Это означает, что серверу не нужно каждый раз запрашивать базу данных для получения пользователя, связанного с данным токеном.
Как работают веб-токены JSON
Когда пользователь успешно входит в систему, используя свои учетные данные, веб-токен JSON получается и сохраняется в локальном хранилище. Всякий раз, когда пользователь хочет получить доступ к защищенному URL-адресу, токен отправляется в заголовке запроса. Затем сервер проверяет допустимый JWT в заголовке авторизации, и, если он найден, пользователю будет разрешен доступ.
Типичный заголовок контента будет выглядеть так:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI
Ниже приведена схема, показывающая этот процесс:
Концепция аутентификации и авторизации
Аутентификация — это процесс идентификации вошедшего в систему пользователя, в то время как авторизация — это процесс определения, имеет ли определенный пользователь право на доступ к веб-ресурсу.
Пример API
В этом руководстве мы собираемся создать простую систему аутентификации пользователей в Django, используя JWT в качестве механизма аутентификации.
Требования
- Джанго
- питон
Давайте начнем.
Создайте каталог, в котором вы будете хранить свой проект, а также виртуальную среду для установки зависимостей проекта.
1
2
3
4
5
|
mkdir myprojects
cd myprojects
virtual venv
|
Активируйте виртуальную среду:
1
|
source venv/bin/activate
|
Создать проект Django.
1
|
django-admin startproject django_auth
|
Установите DRF и django-rest-framework-jwt, используя pip.
1
2
3
|
pip install djangorestframework
pip install djangorestframework-jwt
pip install django
|
Давайте продолжим и добавим DRF в список установленных приложений в файле settings.py
.
Настройте параметры JWT
Чтобы использовать JWT, нам нужно настроить разрешения django-rest-framework для принятия веб-токенов JSON.
В файле settings.py
добавьте следующие конфигурации:
1
2
3
4
5
|
REST_FRAMEWORK = {
‘DEFAULT_AUTHENTICATION_CLASSES’: (
‘rest_framework_jwt.authentication.JSONWebTokenAuthentication’,
),
}
|
Создайте новое приложение под названием users, которое будет обрабатывать аутентификацию и управление пользователями.
1
2
|
cd django-auth
django-admin.py startapp users
|
Добавьте приложение пользователей в список установленных приложений в файле settings.py
.
Настройка базы данных
Мы собираемся использовать базу данных PostgreSQL, потому что она более стабильна и надежна.
Создайте базу данных авторизации и назначьте пользователя.
Переключитесь на учетную запись Postgres на своем компьютере, набрав:
1
|
sudo su postgres
|
Откройте приглашение Postgres и создайте базу данных:
1
2
|
psql
postgres=# CREATE DATABASE auth;
|
Создать роль:
1
|
postgres=# CREATE ROLE django_auth WITH LOGIN PASSWORD ‘asdfgh’;
|
Предоставить пользователю доступ к базе данных:
1
|
postgres=# GRANT ALL PRIVILEGES ON DATABASE auth TO django_auth;
|
Установите пакет psycopg2, который позволит нам использовать настроенную нами базу данных:
1
|
pip install psycopg2
|
Отредактируйте текущую настроенную базу данных SQLite и используйте базу данных Postgres.
01
02
03
04
05
06
07
08
09
10
|
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 для хранения пользовательских данных.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
# 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
и createsuperuser
. Этот класс должен предшествовать классу AbstractBaseUser
мы определили выше. Давайте продолжим и определим это.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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)
|
Миграции
Миграции позволяют обновлять схему базы данных при каждом изменении моделей без потери данных.
Создайте начальную миграцию для нашей модели пользователей и синхронизируйте базу данных в первый раз.
1
2
3
|
python manage.py make migrations users
python manage.py migrate
|
Создание суперпользователя
Создайте суперпользователя, выполнив следующую команду:
1
|
python manage.py createsuperuser
|
Создание новых пользователей
Давайте создадим конечную точку для включения регистрации новых пользователей. Мы начнем с сериализации полей модели пользователя. Сериализаторы предоставляют способ преобразования данных в более простую для понимания форму, такую как JSON или XML. Десериализация выполняет противоположную функцию: преобразование данных в форму, которую можно сохранить в базе данных.
Создайте users / serializers.py и добавьте следующий код.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
# 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 для создания новых пользователей.
В users.views.py добавьте следующее:
01
02
03
04
05
06
07
08
09
10
11
|
# 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)
|
Мы установили для параметра (AllowAny,)
чтобы разрешить любому пользователю (аутентифицированному или нет) доступ к этому URL.
Настройка URL
Создайте файл users/urls.py
и добавьте URL-адрес в соответствии с созданным нами представлением. Также добавьте следующий код.
1
2
3
4
5
6
7
8
|
# 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
, поэтому не забудьте импортировать ее.
1
2
3
4
5
6
7
8
9
|
# 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, это инструмент, который представляет дружественный графический интерфейс для построения запросов и чтения ответов.
Как вы можете видеть выше, конечная точка работает как ожидалось.
Аутентификация пользователей
Мы будем использовать модуль JWT Python Django-REST Framework, который мы установили в начале этого урока. Добавлена поддержка аутентификации JWT для приложений Django Rest Framework.
Но сначала давайте определим некоторые параметры конфигурации для наших токенов и то, как они генерируются в файле settings.py.
01
02
03
04
05
06
07
08
09
10
|
# 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.DecodeEr, если секрет неверен. -
JWT_VERIFY_EXPIRATION
: Устанавливает срок действия True, означая, что токены истекают через определенный промежуток времени. Время по умолчанию составляет пять минут. -
JWT_AUTH_HEADER_PREFIX
: Префикс значения заголовка авторизации, который необходимо отправить вместе с токеном. Мы установили его какBearer
, и по умолчанию используетсяJWT
.
В users/views.py
добавьте следующий код.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@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
поскольку любой может получить доступ к этой конечной точке.
Мы также храним время последнего входа пользователя с этим кодом.
1
2
|
user_logged_in.send(sender=user.__class__,
request=request, user=user)
|
Каждый раз, когда пользователь хочет сделать запрос API, он должен отправить токен в заголовках аутентификации для аутентификации запроса.
Давайте проверим эту конечную точку с Почтальоном. Откройте Почтальон и используйте запрос для аутентификации у одного из созданных вами пользователей. Если попытка входа будет успешной, ответ будет выглядеть так:
Получение и обновление пользователей
Пока что пользователи могут зарегистрироваться и аутентифицировать себя. Однако им также нужен способ извлечения и обновления своей информации. Давайте реализуем это.
В users.views.py
добавьте следующий код.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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, чтобы определить конечную точку следующим образом.
1
2
3
4
5
6
7
|
users/urls.py
from .views import CreateUserAPIView, UserRetrieveUpdateAPIView
urlpatterns = [
url(r’^update/$’, UserRetrieveUpdateAPIView.as_view()),
]
|
Чтобы запрос был успешным, заголовки должны содержать токен JWT, как показано ниже.
Если вы попытаетесь запросить ресурс без заголовка аутентификации, вы получите следующую ошибку.
Если пользователь остается вне времени, указанного в JWT_EXPIRATION_DELTA
не JWT_EXPIRATION_DELTA
запрос, токен истекает, и он должен будет запросить другой токен. Это также продемонстрировано ниже.
Вывод
В этом руководстве рассказывается о том, что необходимо для создания надежной серверной системы аутентификации с помощью JSON Web Tokens.