session
생성session을 이용한 인증에서는 중요한 session 정보를 서버에서 관리한다.
그래서 django에서 session 인증을 사용하면 django_session
테이블이 생성되고 session 정보가 저장된다.
위와 같은 Session based authentication의 단점은 클라이언트에서 session id를 보내올 때마다 그에 맞는 session 정보를 찾기위해 DB를 탐색해야 한다는 것이다. 또한, DB를 app마다 분리 해 놓은 경우에는 더 찾기 힘들고 관리가 힘들다.
이런 단점을 극복하기 위한 방법이 JWT!
JWT
를 생성해 클라이언트로 전달JWT는 서버에 저장 되지 않는다.
JWT 방식에서는 정보를 토큰 안에 저장하기 때문에, django에서 사용할 때 session과 달리 테이블이 생성되지 않는다.
Header-Payload-Signature
{
"typ":"JWT",
"alg":"HS256"
}
{
"token_type": "access",
"exp": 1649145719,
"jti": "1foo2jwt3id4",
"user_id": 123
}
session 인증과 다르게 DB 탐색을 하지 않고, JWT의 Signature를 이용해 검증하기 때문에 더 빠르게 인증 처리가 가능하다.
simplejwt 라이브러리 사용
pip install djangorestframework-jwt
# 소셜 로그인 지원 라이브러리
# pip install django-allauth
# DRF에서 회원가입, 로그인 기능 지원
# 참고 https://velog.io/@jcinsh/DRF-11-Django-REST-Auth
# pip install django-rest-auth
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 인증된 사용자만 접근 가능
# 'rest_framework.permissions.IsAdminUser', # 관리자만 접근 가능
# 'rest_framework.permissions.AllowAny', # 누구나 접근 가능
),
'DEFAULT_AUTHENTICATION_CLASSES': (
# JWT를 기본 인증 방식으로 지정
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework.authentication.TokenAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication',
),
}
# 추가적인 JWT_AUTH 설젇
JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_ALGORITHM': 'HS256', # 암호화 알고리즘
'JWT_ALLOW_REFRESH': True, # refresh 사용 여부
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 유효기간 설정
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28), # JWT 토큰 갱신 유효기간
}
JWT token 발행
...
from rest_framework_jwt.settings import api_settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import update_last_login
...
# JWT 사용을 위한 설정
JWT_PAYLOAD_HANDLER = api_settings.JWT_PAYLOAD_HANDLER
JWT_ENCODE_HANDLER = api_settings.JWT_ENCODE_HANDLER
class LoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
password = serializers.CharField(max_length=128, write_only=True)
token = serializers.CharField(max_length=255, read_only=True)
def validate(self, data):
username = data.get("username")
password = data.get("password", None)
# 사용자 아이디와 비밀번호로 로그인
user = authenticate(username=username, password=password)
if user is None:
return {'username': 'None'}
try:
# Payload 생성
payload = JWT_PAYLOAD_HANDLER(user)
# jwt_token 생성
jwt_token = JWT_ENCODE_HANDLER(payload)
update_last_login(None, user)
except User.DoesNotExist:
raise serializers.ValidationError(
'username and password does not exist'
)
return {
'username': user.username,
'token': jwt_token
}
...
# JWT 사용을 위해 필요
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
# generics cbv
from rest_framework import generics
...
class Login(generics.GenericAPIView):
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid(raise_exception=True):
return Response({"message": "Request Body Error."}, status=status.HTTP_409_CONFLICT)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
if user['username'] == "None":
return Response({"message": "fail"}, status=status.HTTP_401_UNAUTHORIZED)
return Response(
{
"user": UserSerializer(
user, context=self.get_serializer_context()
).data,
"token": user['token']
}
)
from django.urls import path
from django.conf.urls import url
from . import views
urlpatterns = [
path('login', views.Login.as_view()),
]
참고
JWT란? : https://www.qu3vipon.com/django-jwt
djangorestframework-jwt :https://moondol-ai.tistory.com/174?category=865999 / https://velog.io/@kong2520/DRF-JWT-로그인-authentication