kustiktm_kirill
@kustiktm_kirill

DRF SimpleJWT + AbstractBaseUser?

Здравствуйте!
Решил запилить авторизацию на backend'e через библиотеку drf-simplejwt.

До подключения библиотеки уже была реализована своя модель User через AbstractBaseUser:
from django.conf import settings
from django.contrib.auth.models import (
    AbstractBaseUser, BaseUserManager, PermissionsMixin
)
from django.db import models
class UserManager(BaseUserManager):
    def create_user(self, username, email, password=None):
        """ Создает и возвращает пользователя с имэйлом, паролем и именем. """
        if username is None:
            raise TypeError('Users must have a username.')
        if email is None:
            raise TypeError('Users must have an email address.')
        user = self.model(username=username, email=self.normalize_email(email))
        user.set_password(password)
        user.save()
        return user
    def create_superuser(self, username, email, password):
        """ Создает и возввращет пользователя с привилегиями суперадмина. """
        if password is None:
            raise TypeError('Superusers must have a password.')
        user = self.create_user(username, email, password)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(db_index=True, max_length=255, unique=True)
    first_name = models.TextField(max_length=32)
    last_name = models.TextField(max_length=32)
    email = models.EmailField(db_index=True, unique=True)
    telephone = models.CharField(max_length=15)
    avatar = models.ImageField(upload_to='users_avatars')
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']
    objects = UserManager()
    def __str__(self):
        return self.email
    def get_full_name(self):
        return self.first_name + " " + self.last_name
    def get_short_name(self):
        return self.username


И в бд уже был суперюзер.
После подключения drf-simplejwt сделал миграции, настроил urls, вроде все как положено.

general urls.py:
from django.contrib import admin
from django.urls import path, include
from .views import *
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('authentication/', include('authentication.urls'))
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


authentication's urls.py:
from django.urls import path
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView)
from authentication.views import register

app_name = 'authentication'
urlpatterns = [
    path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    # Submit your refresh token to this path to obtain a new access token
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # Register a new user
    path('register/', register, name='register_view'),
]


settings.py:
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'rest_framework_simplejwt.token_blacklist',
    'rest_framework',
    'authentication',
]

AUTH_USER_MODEL = 'authentication.User'

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': False,

    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,

    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',

    'JTI_CLAIM': 'jti',

    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}


Собственно говоря это вроде всё по настройке.
Так в чем гвоздь программы: при попытке аутенфикации он выкидывет мне ошибку в консоли 400 Bad Request.
И больше ничего. Где-то через час решил убрать свою модель User и вернуть стандартную от Django. И о чудо, все заработало!

Можно ли как-то заставить drf-simplejwt работать с собственной моделью User? Очень не хочу делать ForeignKey на другую таблицу, просто смысл JWT потеряется, опять запросы к бд пойдут.
Я откопал djangorestframework-jwt-custom-user на просторах гугла, но либо это не то, либо я читать не умею и не понимаю, как надо использовать.
  • Вопрос задан
  • 286 просмотров
Пригласить эксперта
Ответы на вопрос 1
@maestroexo
Если я правильно понимаю, проблема в том что джанга не видет твоего юзера, добавь в сеттинг
AUTH_USER_MODEL = 'authentication.User'

Я бы использовал более простую конструкцию, без переопределения менеджера
settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
}


SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
    'REFRESH_TOKEN_LIFETIME': timedelta(weeks=4),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': False,
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUTH_HEADER_TYPES': ('Token',),
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
}

AUTH_USER_MODEL = 'account.User'


models.py
from django.contrib.auth.models import AbstractUser


class User(AbstractBaseUser):
    username = models.CharField(db_index=True, max_length=255, unique=True)
    first_name = models.TextField(max_length=32)
    last_name = models.TextField(max_length=32)
    email = models.EmailField(db_index=True, unique=True)
    telephone = models.CharField(max_length=15)
    avatar = models.ImageField(upload_to='users_avatars')
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']
    def __str__(self):
        return self.email
    def get_full_name(self):
        return self.first_name + " " + self.last_name
    def get_short_name(self):
        return self.username
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы