[drf]airbnb-api -14 LoginView(feat.JWT)

Hyeseong·2021년 3월 28일
0

우선 로그인 뷰는 FBV로 만들어 볼게요.
그 이유는 그렇게 복합적이고 많은 기능을 포함하지 않거든요.

로그인뷰 작성 - 1

users/views.py

from django.contrib.auth import authenticate
# 생략

@api_view["POST"]
def login(request):
	username = request.data.get('username')
    password = request.data.get('password')

	if not username or not password:
    	return Response(status=status.HTTP_400_BAD_REQUEST)
	user = authenticate(username=username, password=password)
    

authenticate(username=username, password=password)로그인을 해주는 장고 메서드입니다.
auth모듈에서 가져와 메서드를 임포트 해줄게요.

users/urls.py

from django.urls import path
from . import views
app_name = "users"

urlpatterns = [
    path("", views.UsersView.as_view()),
    path("token", views.login),			# 새롭게 추가
    path('me/', views.MeView.as_view()),
    path('me/favs/', views.FavsView.as_view()),
    path('<int:pk>/', views.user_detail),
]

여기까지 어느 정도 작성을 마쳤다면, 이제 JWT(Json Web Token)로 들어가야해요.

JWT 어떻게 생겼나요?

아래와 같이 생겼어요.

실제 만들어 보고(encode) 만들었던걸 복호화(암호 해동? Decoded!)도 해보조~!

관련 링크 : https://pyjwt.readthedocs.io/en/latest/usage.html

설치

pip install PyJWT

로그인뷰 작성 - 2

users/views.py

import jwt	# 새로추가
from django.contrib.auth import authenticate
from django.conf import settings # 새로추가
# 생략

@api_view["POST"]
def login(request):
	username = request.data.get('username')
    password = request.data.get('password')

	if not username or not password:
    	return Response(status=status.HTTP_400_BAD_REQUEST)
	user = authenticate(username=username, password=password)
    if user is not None:
        encoded_jwt = jwt.encode({'pk':user.pk}, settings.SECRET_KEY , algorithm='HS256')
        return Response(data={'token': encoded_jwt})
    else:
        return Response(status=staut.HTTP_401_UNAUTHORIZED)
    

바디에 정보를 담아서 POST요청을 날리면 token정보를 다시 확인 할 수 있어요.

JWT는 사용하기 쉽기에 인기가 많조. 반대로 사용하기 쉽다는건 복호화 하기도 쉽겠네?라는 지적을 듣기도 쉽습니다. 그래서! 반드시 토큰에는 민감한 정보(비밀번호, 이메일, 이름)를 절대! 넣어서는 안되요. 단순한 테이블의 PK값만 넣어줘서 식별할 수 있는 값만 넣어줘도 충분합니다.

다시한번, 누구나! decoded 할 수 있어요. 그런데 서버는 우선 토큰을 받으면 해당 정보를 까서 무엇인지 확인 할 수 있어요. 그런데 이 서버는 받은 토큰에 어떤 변경사항이 하나라도 있었는지 판단해요.
그래서 실제 우리는 토큰에 어떤정보가 있었는 것보다 중요한건 누가 token을 건들지 않았는지?! 확인하는 것이 더 중요해요!

이제 복호화 과정을 거치고 쭉쭉 거치고 우리 API가 JWT를 알아 먹게 해보조.

링크 - https://www.django-rest-framework.org/api-guide/authentication/

drf auth

drf에서는 기본적으로 2가지 authentication을 하는데요
베이직이랑! 세션!

세션의 경우는 이럴때 사용되는 예가 있습니다.
바로 browsable API를 우리가 사용할 때가 대표적이죠.

사용하기 전에 일단 settings.py에 설정 작업을 해줘야합니다.

settings.py

# 생략
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

만약 "rest_framework.authentication.SessionAuthentication",한줄을 지워버리면 어떻게 될까요?

로그인하라고 팝업창이 뜨네요.
필요한 "rest_framework.authentication.SessionAuthentication",은 놔두고 베이직은 지워버릴게요.

# 생략
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE':10,

    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
    ],
} 

header에 토큰 넣어주기

Header안에 Authorization키를 잡아주고 값은 X-JWT 토큰값(중간에 공백 존재!)라고 입력할게요.

인증이 유효하지 않다고 나오종? DEFAULT_AUTHENTICATION_CLASSES에 JWT에 연관된 녀석을 넣어주지 않아서 그래요.

커스텀 Authentication

직접 JWT 인증을 위한 클래스를 커스터마이징을 해볼게요.

  • config 디렉토리 안에 authentication.py 파일을 만들게요.

그리고 위에서 봤던 Example 소스 코드를 일단 그대로 복붙하고 아래와 같이 소스 코드를 변경할게요.

import jwt
from django.conf import settings
from rest_framework import authentication
from rest_framework.response import Response
from users.models import User


class JWTAuthentication(authentication.BaseAuthentication): # DRF의 Token방식과 JWT의 차이 DB저장 유무(JWT는 저장하지 않음)
    def authenticate(self, request):                        # 서버 측면에서 더 효율적임 2만명이 로그인을 해도 디비에서 저장하는 것은 아무것도 없음
        try:                                                # 리프레쉬 토큰이 현재 구현 되지 않은 부분이 있음
            token = request.META.get("HTTP_AUTHORIZATION") # 헤더에 관한 정보는 MEATA속성에 있음
            if token is None:
                return None
            xjwt, jwt_token = token.split(" ") #관습적으로 많이 사용함 
            decoded = jwt.decode(jwt_token, settings.SECRET_KEY, algorithms=["HS256"])
            pk = decoded.get("pk")
            user = User.objects.get(pk=pk)
            return (user,None) # tuple, list로 안넘기면 오류남 cannot unpack non-iterable User object
        except ValueError:
            return None
        except jwt.exceptions.DecodeError:
            raise exceptions.AuthenticationFailed(detail="JWT Format Invalid")
            # return Response(status=status.HTTP_401_UNAUTHORIZED)
        except User.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

그리고 나서

settings.py

"config.authentication.JWTAuthentication",이걸 넣어줄게요.

# 생략
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE':10,

    "DEFAULT_AUTHENTICATION_CLASSES": [
        "config.authentication.JWTAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ],
} 

아래와 같이 endpoint로 이동해서 원하는 정보를 쫘악~ 볼수 있는거조.

profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글