header = { "alg": "HS256", "typ": "JWT"}
INSTALLED_APPS = [
…
'users.apps.UsersConfig',
]
from datetime import datetime
from pydantic import BaseModel
class Author(BaseModel):
first_name: str
last_name: str
date_birth: datetime
biography: str
class Book(BaseModel):
title: str
annotation: str
date_publishing: datetime
author: Author
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=2),
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True
}
INSTALLED_APPS = [
…
'users.apps.UsersConfig',
'rest_framework',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
]
from django.contrib import admin
from django.urls.conf import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/users/', include('users.urls'))
]
from typing import Any, Type, Union
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
"""
Менеджер для переопределенной модели юзера.
"""
use_in_migrations = True
def create_user(self, email: str, password: str, **kwargs: Union[str, Any]) -> Type[BaseUserManager]:
"""
Метод менеджера для создания обычного пользователя.
"""
if not email:
raise ValueError("Please, input email address")
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email: str, password: str, **params: Union[str, Any]) -> Type[BaseUserManager]:
"""
Метод менеджера для создания суперюзера.
"""
params.setdefault("is_staff", True)
params.setdefault("is_superuser", True)
params.setdefault("is_active", True)
if params.get("is_staff") is not True:
raise ValueError("superuser must have a is_staff=True")
if params.get("is_superuser") is not True:
raise ValueError("superuser must have a is_superuser=True")
return self.create_user(email, password, **params)
from typing import List
from django.db import models
from django.contrib.auth.models import AbstractUser
from users.managers import UserManager
from rest_framework_simplejwt.tokens import RefreshToken
class User(AbstractUser):
"""
[User]
Переопределенный класс пользователя. Использует кастомный менеджер.
"""
username = None
# Поле email будет использоваться для идентификации пользователя в системе
email = models.EmailField(unique=True)
# Указывает какое поле используется для входа в систему
USERNAME_FIELD = "email"
REQUIRED_FIELDS: List = []
# Указывает, какой менеджер использовать для данной модели
objects = UserManager()
class Meta:
verbose_name = "Пользователь"
verbose_name_plural = "Пользователи"
app_label = 'users'
@property
def access_token(self) -> str:
"""
Позволяет получить токен доступа из экземпляра модели User.
:return: str
"""
return str(RefreshToken.for_user(self).access_token)
@property
def refresh_token(self) -> str:
"""
Позволяет получить рефереш токен из экземпляра модели User.
:return: str
"""
return str(RefreshToken.for_user(self))
def __str__(self) -> str:
"""
:returns:
[str]: Отвечает за корректное отображение объекта.
"""
return self.email
AUTH_USER_MODEL = "users.User"
from typing import Dict
from rest_framework import serializers
from users.models import User
class RegistrationSerializer(serializers.ModelSerializer):
"""
Сериализатор для регистрации нового пользователя
"""
password = serializers.CharField(
max_length=128,
min_length=8,
write_only=True
)
access_token = serializers.CharField(max_length=255, read_only=True)
refresh_token = serializers.CharField(max_length=255, read_only=True)
class Meta:
model = User
fields = ['email', 'first_name', 'last_name', 'password', 'access_token', 'refresh_token']
def create(self, validated_data: Dict) -> User:
# Используется метод из кастомного менеджера
return User.objects.create_user(**validated_data)
class UserInfoSerializer(serializers.ModelSerializer):
"""
Сериализатор для получения основной информации о пользователе
"""
class Meta:
model = User
fields = ['email', 'first_name', 'last_name'
from rest_framework import status
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from users.serializers import RegistrationSerializer, UserInfoSerializer
class RegistrationAPIView(APIView):
# Доступ к регистрации должны иметь все пользователи
permission_classes = [AllowAny]
serializer_class = RegistrationSerializer
def post(self, request: Request) -> Response:
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
class UserInfoAPIView(APIView):
serializer_class = UserInfoSerializer
permission_classes = [IsAuthenticated]
def get(self, request: Request) -> Response:
return Response(self.serializer_class(request.user).data, status=status.HTTP_200_OK)
from rest_framework_simplejwt.token_blacklist.models import OutstandingToken, BlacklistedToken
class ResetTokenAPIView(APIView):
"""
Добавляет все refresh токены пользователя в черный список
"""
def post(self, request: Request) -> Response:
tokens = OutstandingToken.objects.filter(user_id=request.user.id)
for token in tokens:
t, _ = BlacklistedToken.objects.get_or_create(token=token)
return Response(status=status.HTTP_205_RESET_CONTENT)
from users.views import RegistrationAPIView, UserInfoAPIView, ResetTokenAPIView
urlpatterns = [
...
path('reset-all-token/', ResetTokenAPIView.as_view(), name='reset-all-token')
]