노마드 코더 Airbnb 클론 코딩

노마드 코더 에어비앤비 클론 코딩 #15 AUTHENTICATION

gogi masidda 2022. 12. 1. 20:35

Authentication

django는 우리가 바로 사용할 수 있는 인증시스템이 기본으로 있다.

로그인하면 django는 백엔드에서 세션을 생성하고 자동으로 쿠키도 준다. 매번 django 웹사이트를 방문할 때마다, 쿠키는 django로 가고, django는 쿠키를 읽어서 request.user에 user와 쿠키 정보를 함께 넣는다. 

 

이런 기본 인증 시스템이 아닌 직접 커스텀 인증을 만들 수 있다. 이런 방법에는 토큰 인증, JWT인증 등이 있다.

 

Custom Authentication

#config/authentication.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from users.models import User

class TrustMeBroAuthentication(BaseAuthentication):

    def authenticate(self, request): #여기 request에는 user가 없다. request의 header에서 유저를 찾아야한다. 유저를 못찾으면 None을 반환해야함.
        username = request.headers.get('Trust-me')
        if not username: #header에서 유저를 찾지 못했을 경우
            return None
        try:
            user = User.objects.get(username=username)
            return (user, None) #규칙임
        except User.DoesNotExist: #user목록에 로그인 요청한 user가 없을 경우
            raise AuthenticationFailed(f'No user {username}')

위의 코드처럼 우리만의 Authentication class를 생성한다. 마지막에 '(user,None)'만 반환해주면 된다. 

Authentication class에서 반환하는 user가 바로 views에서 받게되는 request.user이다.

 

BaseAuthentication을 상속(확장)받은 모든 클래스는 'authenticate'라는 method를 override 해야한다. 

#config/settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
        "config.authentication.TrustMeBroAuthentication",
    ]
}

이후에는 이처럼 직접 만든 Authentication class를 인증 방식에 추가한다. 그러면 API request가 있을 때마다 views의 코드가 실행되기 전에 Authentication class를 자동으로 호출할 것이다.

 

Token Authentication

django rest_framework에는 이미 token authentication이 있다.

=> config/settings.py. THIRD_PARTY_APPS 아래에 'rest_framework.authtoken'을 추가한다. 

     -> admin 패널에 새로운 요소가 생길 것이다. 그래서 authtoken을 import하면 migration 파일도 생긴다. 

     -> 터미널에 'python manage.py migrate'를 해주어야한다.

#config/settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        ...
        "rest_framework.authentication.TokenAuthentication",
		...
    ]
}
#users/ urls.py 
...
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
				...
                path("token-login",obtain_auth_token)
                ...
	]

obtain_auth_token은 username과 password를 보내면 token을 반환한다. 그러면, user에게 토큰을 주고, 그 토큰은 데이터 베이스에 저장된다. request가 있으면 Rest Framework는 token을 찾아서 request에게 user가 누군지 알려줄 것이다.

usernamer과 password를 주고 토큰 받기
토큰을 주고 유저정보 받기

JSON WEB TOKEN (JWT)

①JSON WEB TOKEN (JWT) Encode

암호화된 정보(유저에 관한 정보)를 담고있는 토큰을 유저에게 준다. 유저는 그 토큰을 가지고 있다가 필요한 때 다시 준다. 유저가 토큰을 주면, django는 그 토큰을 열어서 정보를 확인한다. 그래서 데이터베이스에 아무 것도 저장할 필요가 없다. 데이터베이스의 공간을 차지하지 않는다.

JWT에는 강제 로그아웃 기능이 기본으로 있지 않다. 강제 로그아웃을 하고 싶다면, Auth Token이나 기본 세션/쿠키 인증을 사용하는게 좋다.

 

#users/urls.py

urlpatterns = [
	...
    path("jwt-login", views.JWTLogIn.as_view()), #JWT Token
	...
]
#users/views.py

import jwt

class JWTLogIn(APIView):

    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        if not username or not password:
            raise ParseError
        user = authenticate( #username과 password가 맞지 않으면 user를 리턴하지 않음.
            request, 
            username=username, 
            password=password,
        )
        if user:
            token = jwt.encode(
                {"pk": user.pk}, #토큰에 이 정보를 포함.
                settings.SECRET_KEY, #아무나 토큰에 담긴 정보를 볼 수 없도록 SECRET_KEY로 서명한다.
                algorithm="HS256" #토큰을 암호화할 때 이 알고리즘을 쓰겠다.
            )
            return Response({"token": token})
        else:
            return Response({"error":"Wrong Password"})

SECRET_KEY는 config/settings.py에서 볼 수 있다. 이것은 django가 유저에게 제공하는 쿠키나 세션에 서명할 때 사용하는 것이다. 그래서 이것은 다른 사람에게 공유되면 안된다. 

username과 password를 주고 token을 받음

token에 유저 정보를 담고 있기 때문에 이전 방식의 토큰보다 길이가 길다.

 

②JSON WEB TOKEN(JWT) Decode

#config/authentication.py

import jwt
from django.conf import settings

class JWTAuthentication(BaseAuthentication):

    def authenticate(self, request):
        token = request.headers.get('Jwt')
        if not token:
            return None
        decoded = jwt.decode(
            token,
            settings.SECRET_KEY, #복호화하는데 필요한 SECRET_KEY
            algorithms= ["HS256"], #Encode할 때 사용한 algorithm
            )
        pk = decoded.get('pk')
        if not pk:
            raise AuthenticationFailed("Invalid Token")
        try:
            user = User.objects.get(pk=pk)
            return (user,None)
        except User.DoesNotExist:
            raise AuthenticationFailed("User Not Found")

토큰을 받고 유저 정보를 보여줌

 

Environment Files

.env 파일을 만들어서, settings.py의 SECRET_KEY를 .env 파일 안에 붙여 넣는다.

-> 터미널에 'python add django-environ'을 입력해서 라이브러리를 설치한다.

->

#config/settings.py

import os
import environ

env = environ.Env()

BASE_DIR=Path(__file__).resolve().parent.parent

#environ.Env.read_env(경로)
environ.Env.read_env(os.path.join(BASE_DIR,".env"))

...

SECRET_KEY=env("SECRET_KEY")
environ.Env.read_env(경로)에서 경로를 찾는 법은 'print(f"{BASE_DIR}/.env")'를 하고 터미널을 확인하는 방법이 있다.
그리고 더 쉬운 방법은 os를 이용하는 것으로 'environ.Env.read_env(os.path.join(BASE_DIR,".env"))' 이렇게 작성하면 알아서 BASE_DIR의 경로에 .env를 /와 함께 결합해준다.
이렇게 SECRET_KEY를 '.env'라는 파일에 두어 다른 사람과 공유되지 않도록 할 수 있다.
728x90