티스토리 뷰

지난시간 로그인 처리를 훌륭하게 마무리 했다.

이번 시간은 쉬어가는 코너로 JWT 세팅 중 그다지 중요하진 않지만 해놓으면 편한 그런 셋팅을 마무리하려고 한다.

 

일단 필요한 기능이 "JWT_GET_USER_SECRET_KEY" 이거다. 서명할 때 사용자 개인 키를 적용해 서명을 암호화할 때 사용하게 한다. 이 기능이 있어야 나중에 로그아웃도 만들 수 있다.

 

account 앱에 utils.py 파일을 하나 만들자.

 

이렇게 만들고 utils.py 에 아래와 같이 코딩하자.

account/utils.py

def get_secret_key(model):
    return model.secret

그냥 이렇게만 코딩해도 괜찮다. 이렇게 코딩하고 settings.py 파일을 열자.

funny_picture/settings.py

#
#  생략
#

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=168),
    'JWT_ALLOW_REFRESH': True,
    'JWT_GET_USER_SECRET_KEY': 'account.utils.get_secret_key',
    'JWT_VERIFY': True,
    'JWT_AUTH_HEADER_PREFIX': 'FNY'
}

#
#  생략
#

이렇게 하면 DB에 지정되어있는 account_user 테이블에 저장되어 있는 secret 값이 서명에 사용되게 된다. 그래서 이 값은 외부에 노출 되면 안되므로 나중에 Serialize에서 secret 값을 지워주도록 하자.

 

account/serializers.py

#
#  생략
#

class UserSerializer(serializers.ModelSerializer):
    auths = AuthSerializer(many=True, read_only=True)

    class Meta:
        model = User
        fields = [
            'id'
            , 'email'
            , 'status'
            , 'is_active'
            , 'last_login'
            , 'auths'
            , 'created_at'
            , 'updated_at'
        ]

#
#  생략
#

이렇게 secret 값을 지워준다.

 

이렇게 수정 한 뒤에 서버를 재기동하고 다시 로그인해보도록 하자.

 

이렇게 보면 토큰에 무슨 변화가 생긴것 같아 보이진 않을거다. 그런데 이 토큰을 https://jwt.io/ 에 가져가서 확인해보면 이전과 다른 결과를 볼 수 있다. 

secret key 적용 전
Secret key 적용 후

이렇게 서명의 유효성이 달라지게 된다.

 

 

로그아웃 구현하기

사용자가 이제 "로그아웃"을 했다고 치자. 그러면 이전에 사용하던 토큰은 사용할 수 없어야한다.

그렇다면 어떻게 해야할까?

 

간단하게 사용자의 Secret Key를 변경하면 서명이 유효하지 않게 되니 더이상 토큰을 사용할 수 없게 되어버린다.

그래서 사용자 로그아웃 API를 추가해 만들어주자.

 

account/view.py

import uuid

from django.shortcuts import render
from django.contrib import auth

from rest_framework             import status
from rest_framework.decorators  import api_view
from rest_framework.views       import APIView
from rest_framework.response    import Response
from rest_framework.permissions import IsAuthenticated

from .serializers import UserSerializer
from .models import User

class HelloView(APIView):
    name = 'hello-view'

    def get(self, request, *args, **kwargs):
        return Response({'hello': 'world!'})

class UserInfo(APIView):
    name = 'user-info'

    permission_classes = [IsAuthenticated]

    def get(self, request, *args, **kwargs):
        user_id = request.query_params.get("id")
        query = User.objects.get(id=user_id)
        
        serializer = UserSerializer(query, many=False)

        return Response( serializer.data )
        
#사용자 로그아웃
@api_view(['POST'])
def user_logout(request):
    if request.user:
        user = request.user
        User.objects.filter(email=user).update(secret=uuid.uuid4())
        auth.logout(request)
    return Response(status=status.HTTP_200_OK)

상단에 uuid와 api_view를 import한 코드 놓치면 안되고 제일 하단에 user_logout 메서드가 추가된 부분을 잘 파악하면 된다. 사용자 정보가 있는 상태로 로그아웃 API를 호출하면 사용자 정보에서 secret 값을 새로운 uuid 값을 넣어서 수정하는 코드다. 그 뒤에 세션에 남아있는 사용자 정보도 auth.logout 메서드를 통해 지워버린다.

 

account/urls.py

from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token
from .views import *

urlpatterns = [
    url(r'^v1/hello', HelloView.as_view(), name=HelloView.name)
    , url(r'^v1/get-user', UserInfo.as_view(), name=UserInfo.name)
    , url(r'^v1/get-auth-token', obtain_jwt_token)   # 사용자 로그인(토큰발급)
    , url(r'^v1/token-verify', verify_jwt_token)   # 사용자 토큰 유효성 검사
    , url(r'^v1/token-refresh', refresh_jwt_token)  # 사용자 토큰 갱신
    , url(r'^v1/logout', user_logout) # 사용자 로그아웃
]

제일 아래 사용자 로그아웃 메서드 호출하는 URL을 추가해줬다. 이제 테스트 해보도록 하자.

 

 

로그아웃 하기전 secret 값은 위와 같다.

그리고 로그아웃을 호출해보도록 하자.

 

정상적으로 200 OK 값을 응답 받았다. 그럼 이제 DB 를 확인해 secret 값이 변경 되었는지 확인해보자.

 

값이 정상적으로 바뀐 것을 확인할 수 있다.

아, 그리고 model.py 에 코드를 추가해줘야한다.

 

account/models.py

# 사용자 계정 테이블 모델
class User(AbstractBaseUser):
    id           = models.BigAutoField(primary_key=True)
    email        = models.EmailField(max_length=254, unique=True) # 이메일 주소
    secret       = models.UUIDField(default=uuid.uuid4) # 사용자 서명용 비밀키
    status       = models.CharField(max_length=10, default=USER_STATUS.get('ACTIVE', 'ACTIVE')) # 사용자 현재 상태
    is_active    = models.BooleanField(default=False) # 계정 활성화 여부
    created_at   = models.DateTimeField(auto_now_add=True) # 계정 생성일
    updated_at   = models.DateTimeField(auto_now=True) # 계정 수정일
    
    objects = UserManager() # 사용자 정보를 관리하는 클래스는 지정한다.

    USERNAME_FIELD = 'email' # 사용자 이름으로 사용될 필드의 이름을 지정한다.

    def __str__(self):
        return self.email

    # 사용자 PK 값을 가져오기위한 함수
    def get_id(self):
        return self.id

중간에 "def __str__(self)" 이 코드 꼭 추가해준다음에 로그아웃을 테스트 해보도록 하자. 잘 안되면 로그인부터 다시 테스트 해보면 잘 될거다.

 

내가 저 str 코드 빼먹어서 중간에 곤란곤란했었다.

 

암튼 이렇게 까지 하면 일단 대충 로그인, 로그아웃, 서명키 설정까지 마무리 되었다.

이제 관리자 페이지 로그인을 만들어보도록 하자.

 

그럼 오늘은 이만 안녕!

댓글
댓글쓰기 폼