본문 바로가기
Django

[DRF] JWT로 회원가입, 로그인 기능 구현

by eoruadl 2023. 3. 7.

Django DRF 라이브러리를 사용하여 JWT 회원가입, 로그인 기능을 구현해보았다.

 

먼저 SimpleJWT docs를 통해 기본 환경설정을 해주었다.

https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html

 

Getting started — Simple JWT 5.2.2.post13+g960ab2b documentation

Cryptographic Dependencies (Optional) If you are planning on encoding or decoding tokens using certain digital signature algorithms (i.e. RSA and ECDSA; visit PyJWT for other algorithms), you will need to install the cryptography library. This can be insta

django-rest-framework-simplejwt.readthedocs.io

 

기본 환경설정을 끝낸 후

회원 model은 장고에서 기본적으로 제공하는 User model을 사용했다. 왜냐하면 회원가입 시 필요한 정보를 ID, PASSWORD, e-mail이면 충분하다고 생각했다. 추후 필요한 정보는 프로필 테이블을 따로 생성하여 저장할 생각이다.

 

기본 User 모델 필드 중 사용할 필드는 username, email, password 세가지 이다. (username을 ID로 활용)

 

다음 회원가입 Serializer를 구현한다.

from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password

from rest_framework import serializers
from rest_framework.validators import UniqueValidator



class RegisterSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(
        required=True,
        validators=[UniqueValidator(queryset=User.objects.all())],
    )

    password = serializers.CharField(
        write_only=True,
        required=True,
        validators=[validate_password],
    )
    password2 = serializers.CharField(write_only=True, required=True)

    class Meta:
        model = User
        fields = ('username', 'password', 'password2', 'email')

    def validate(self, data):
        if data['password'] != data['password2']:
            raise serializers.ValidationError(
                {"password": "Password fields didn't match."})
        
        return data

    def create(self, validated_data):
        user = User.objects.create_user(
            username=validated_data['username'],
            email=validated_data['email'],
        )

        user.set_password(validated_data['password'])
        user.save()
        return user

시리얼라이저에서 비밀번호가 일치하는지 검증하는 과정을 함수로 구현했고 검증이 완료되면 생성하도록 함수로 구현했다.

 

다음으로 views 파일에 APIView를 구현했다.

from rest_framework.views import APIView
from .serializers import RegisterSerializer
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework import status
from rest_framework.response import Response

class RegisterAPIView(APIView):
    def post(self, request):
        serializer = RegisterSerializer(data=request.data)
        if serializer.is_valid():
            user = serializer.save()

            token = TokenObtainPairSerializer.get_token(user)
            refresh_token = str(token)
            access_token = str(token.access_token)
            res = Response(
                {
                    "user": serializer.data,
                    "message": "register success",
                    "token": {
                        "access": access_token,
                        "refresh": refresh_token,
                    },
                },
                status=status.HTTP_200_OK,
            )

            res.set_cookie("access", access_token, httponly=True)
            res.set_cookie("refresh", refresh_token, httponly=True)

            return res
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

POST 방식으로 요청이 들어오면 데이터를 토큰에 담고 access 토큰과 refresh 토큰을 생성하여 쿠키에 담아주었다.

 

다음으로 urls 설정해준다.

# users/urls.py

from django.urls import path
from .views import RegisterAPIView

urlpatterns = [
    path('register/', RegisterAPIView.as_view()),
]

# base/urls.py

from django.contrib import admin
from django.urls import path, include

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/users/', include('users.urls')),
]

 

마이그레이션을 진행하고 api 테스트를 해보겠다. / Insomnia를 사용하여 테스트를 진행하였다.

회원가입이 정상적으로 이루어지며 access토큰과 refresh토큰을 생성한 것을 확인할 수 있다.

 

다음으로 로그인 API를 구현해보았다.

먼저 시리얼라이저를 구현하자.

class LoginSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'password', 'email')

ModelSerializer를 이용하여 User 모델에서 세가지 필드를 받아왔다.

 

다음 views파일에서 API를 구현하자.

from rest_framework.views import APIView
from .serializers import RegisterSerializer, LoginSerializer
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework import status
from rest_framework.response import Response

from django.contrib.auth import authenticate

class LoginAPIView(APIView):
    def post(self, request):
        user = authenticate(
            username = request.data.get("username"),
            password = request.data.get("password"),
        )
        if user is not None:
            serializer = LoginSerializer(user)
            token = TokenObtainPairSerializer.get_token(user)
            refresh_token = str(token)
            access_token = str(token.access_token)
            res = Response(
                {
                    "user": serializer.data,
                    "message": "Login success",
                    "token": {
                        "access": access_token,
                        "refresh": refresh_token,
                    },
                },
                status=status.HTTP_200_OK,
            )
            return res
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST)

POST방식으로 요청이 들어오면 username과 password를 확인하고 토큰을 확인한다.

 

url을 설정하고 구현이 잘됬는지 확인해보자.

urlpatterns = [
    path('register/', RegisterAPIView.as_view()),
    path('login/', LoginAPIView.as_view()),
]

로그인이 잘 되는것을 확인할 수 있다.

 

다음으로 로그아웃 API를 구현해보자

로그아웃은 시리얼라이저를 따로 구현할 필요가 없고 POST 방식으로 요청이 들어오면 쿠키에서 토큰을 제거해주면 로그아웃이 되도록 구현해보았다.

# users/views.py

class LogoutAPIView(APIView):
    def post(self, request):
        response = Response({
            "message": "Logout success"
        })
        response.delete_cookie("access")
        return response

로그아웃도 잘 되는것을 확인할 수 있다.

댓글