[drf, djangorestframework-simplejwt] username 대신 email로 인증하기

oen·2022년 3월 10일
0

🍡 Django

목록 보기
3/4

# access token, refresh token


djangorestframework-simplejwt 를 이용해서 access token, refresh token으로 사용자를 인증할 예정

# 코드

django app 이름: account라는 가정하에 진행

requirements.txt

djangorestframework-simplejwt==5.1.0

djangorestframework-simplejwt 문서:
https://django-rest-framework-simplejwt.readthedocs.io/en/latest/index.html

account/models.py

from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser


class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifiers
    for authentication instead of username
    """

    def create_user(self, email, password, **extra_fields):
        """
        Create and save a User with the given email and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(email=self.normalize_email(email), **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self.create_user(email=self.normalize_email(email), password=password, **extra_fields)


class CustomUser(AbstractUser):
    username = None
    email = models.EmailField('email address', unique=True)
    is_admin = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = CustomUserManager()

    def __str__(self):
        return self.email

account/auth_backends.py

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend


class EmailBackend(ModelBackend):
    def authenticate(self, request, **kwargs):
        UserModel = get_user_model()
        try:
            email = kwargs.get('email', None)
            if email is None: # comment 1)
                email = kwargs.get('username', None)

            user = UserModel.objects.get(email=email)
            if user.check_password(kwargs.get('password', None)):
                return user

        except UserModel.DoesNotExist:
            return None
        return None

comment 1)
admin 페이지에서 createsuperuser로 만든 계정으로 로그인 하는 경우에 'email' 대신 'username' 에 email 값이 들어오기 때문에, email이 None인 경우 'username' 값을 email 변수에 저장하도록 따로 처리해야 한다.

settings.py

REST_FRAMEWORK = {
    # 각 요청에 대한 허용/거부
    # 등록 된 사용자 만 API에 액세스 할 수 있도록
    # 인증 된 사용자에 대한 액세스를 허용하고 인증되지 않은 사용자에 대한 액세스를 거부하는걸
    # DEFAULT_PERMISSION_CLASSES 설정을 사용하여 전체적으로 설정
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    # 유저 식별
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}

...

# make CustomUser the default user for Django authentication
AUTH_USER_MODEL = 'account.CustomUser'
# tell Django to use EmailBackend as the default authentication backend
AUTHENTICATION_BACKENDS = ['account.auth_backends.EmailBackend']

account/serializers.py

from django.contrib.auth import get_user_model

from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer as JwtTokenObtainPairSerializer


class TokenObtainPairSerializer(JwtTokenObtainPairSerializer):
    username_field = get_user_model().USERNAME_FIELD


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = (
            'email',
            'password',
        )

account/views.py

from django.contrib.auth import get_user_model

from rest_framework import status
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenObtainPairView

from account.serializers import UserSerializer, TokenObtainPairSerializer


class SignupView(APIView):
    http_method_names = ['post']

    permission_classes = (permissions.AllowAny,) # 회원가입은 인증 필요없다

    def post(self, *args, **kwargs):
        serializer = UserSerializer(data=self.request.data)
        if serializer.is_valid():
            get_user_model().objects.create_user(**serializer.validated_data)
            return Response(status=status.HTTP_201_CREATED)
        return Response(status=status.HTTP_400_BAD_REQUEST, data={'errors': serializer.errors})


class EmailTokenObtainPairView(TokenObtainPairView):
    serializer_class = TokenObtainPairSerializer

account/urls.py

from django.urls import path
from . import views

from rest_framework_simplejwt.views import TokenRefreshView

app_name = 'account'

urlpatterns = [
    path('signup/', views.SignupView.as_view()), # POST: 회원가입

    path('token/', views.EmailTokenObtainPairView.as_view()), # POST: email, password로 로그인(refresh, access 발급)
    path('token/refresh/', TokenRefreshView.as_view()), # POST: refresh로 access 발급
]

account/admin.py

from django.contrib import admin
from django.contrib.auth import get_user_model


admin.site.register(get_user_model())

# Postmana 테스트

  • POST http://localhost:8000/account/signup/

  • POST http://localhost:8000/account/token/

  • POST http://localhost:8000/account/token/refresh/

    +

settings.py

SIMPLE_JWT = {
    'ROTATE_REFRESH_TOKENS': True,
}

ROTATE_REFRESH_TOKENS을 True로 설정하면 새로운 refresh 토큰도 같이 리턴된다

profile
🐾

0개의 댓글