TIL(22/11/23)

김규현·2022년 11월 24일
0

💻 Today I Learn

어제 했던 회원가입 및 탈퇴 기능에 이어 JWT(JsonWebToken)을 활용한 로그인 기능을 구현해보았다.

JWT란?

모바일이나 웹의 사용자 인증을 위해 사용되는 암호화된 토큰으로 클라이언트에서 HTTP request 헤더에 토큰을 넣어 보내면 서버는 헤더에 포함되어 있는 토큰의 정보로 사용자를 인증한다.
simple-jwt 공식 문서

JWT를 사용하는 이유

"로그인" 이라는 과정은 서버와 클라이언트가 데이터를 주고 받는 통신 방법이 필요한데 HTTP라는 프로토콜(통신 방법)을 사용한다.

HTTP의 특성상 서버와 클라이언트가 한 번 통신하고 난 후 연결이 유지되지 않고 끊어져버리게 되는데 로그인을 했더라도 이전에 통신한 대한 정보가 남아있지 않기 때문에 서버에 요청을 보내기 위해서는 매번 자신이 누구인지 인증을 해야한다.

크게 세션 방식과 토큰 방식이 있는데 이 두가지 방식의 차이점은 세션의 경우 서버의 메모리, 데이터베이스와 같은 서버에 사용자의 정보를 유지하는 방식으로 서버의 확장성이 떨어지고, 서버의 자원(세션을 저장, 유지할 공간)이 많이 필요하다.

하지만 토큰 방식은 사용자가 로그인을 하면 서버에서 발행해주는 토큰을 가지고
브라우저의 저장소에 토큰을 유지시키는 방법으로 서버에 저장을 하지 않고, 요청이 들어왔을 때 해당 토큰이 유효한지만 확인하면 매번 서버로 사용자가 누군지 요청할 필요가 없다.

JWT를 사용하기 위한 설정

우선 drf에서 JWT를 사용하기 위해 패키지를 인스톨 해준다.

pip install djangorestframework-simplejwt

설치한 simplejwt 패키지를 installed_apps에 추가해준다.

# settings.py
INSTALLED_APPS = [
    'rest_framework_simplejwt',
]

settings.py의 REST_FRAMEWORK에서 사용자에 대한 기본 인증 클래스를 JWTAuthentication로 설정하여 JWT 토큰으로 사용자를 인증한다.

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

SIMPLE_JWT의 환경 변수들을 설정하여 토큰의 유효 기간 등 다양한 설정을 할 수 있다.

# settings.py
from datetime import timedelta

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

    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    'LEEWAY': 0,

    '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',
    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

    'JTI_CLAIM': 'jti',

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

JWT 토큰이 발행/재발행 되는 API를 path에 연결해준 다음 해당 url로 요청이 들어왔을 때 access 토큰과 refresh 토큰이 발행된다.

# urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

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

JWT를 커스텀하는 방법

아래와 같이 TokenObtainPairSerializer를 상속받아 Serializer를 커스텀 할 수 있고, views.py에서 TokenObtainPairView를 상속받아 커스텀한 Serializer를 사용하는 class를 만들 수 있다.

# serializers.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        # Add custom claims
        token['name'] = user.name
        token['email'] = user.email
        token['phone'] = user.phone
        token['msg'] = "some masage"
        # ...

        return token

# views.py
class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer
profile
웹개발 회고록

0개의 댓글