[Django] 4일차. Django Authentication

김민정·2024년 3월 11일

Django

목록 보기
4/8
post-thumbnail

01. Django BasicAuth (Session 로그인)

아래 코드와 같이 loginlogout에 관한 토큰도 추가해준다. 이전에는 DRF(Django Rest Framwork) 토큰만 작성되어 있었다.

  • users/urls.py

    from django.urls import path
    from . import views
    
    urlpatterns = [
            path("login", views.Login.as_view()),  # django session login
        path("logout", views.Logout.as_view()) # django session logout
    ]

1. Login

  • POST 생성

    • users/views.py

      # django의 session을 활용한 로그인
      from rest_framework.exceptions import ParseError
      from django.contrib.auth import authenticate, login
      from rest_framework import status
      
      # api/v1/users/login
      class Login(APIView):
          def post(self, request):
              username = request.data.get("username")
              password = request.data.get("password")
              
              if not username or not password:
                  raise ParseError()
      
                      # 위 아래 둘 중 하나 사용
      
              # if not (username and password):
                  # raise ParseError()
      
              user = authenticate(request, username=username, password=password)
              print(user)
      
              if user:
                  # 아래 코드 실행시 sessionid, csrftoken이 생성
                  # sessionid는 사용자 세션과 관련된 상태 정보를 식별하기 위한 고유한 식별자입니다.
                  # 이를 클라이언트(웹 브라우저)에 쿠키로 저장함으로써,
                  # 브라우저가 서버에 요청할 때마다 해당 세션과 관련된 데이터를 식별하여 사용자 상태를 유지할 수 있습니다.
                  # 단, localhost:3000에서는 실행 안됨.
      
                  # csrftoken: csrf공격 방지를 위해 필요한 토큰
                  # ex) 은행앱에 로그인된 상태에서 악의적인 사이트에서 어떤 행동들을 하면
                  # 쿠키에 있는 값을 털어서 해당 사용자인척 은행 API로 송금 요청을 시도
                  # 일반적으로 Django의 기본 설정에서 세션과 CSRF 토큰은 세션 쿠키와 관련된 도메인과 경로에 한정하여 사용됩니다.
                  # 따라서 http://127.0.0.1:3000/에서 로그인한 후 다른 URL로 이동하면 해당 URL과 경로에 대한 세션과 CSRF 토큰은 적용되지 않습니다.
                  login(request, user)
                  return Response(status=status.HTTP_200_OK)
              else:
                  return Response(status=status.HTTP_403_FORBIDDEN)
      • login(request, user, backend=None) ⇒ 세션 시작
        • 서버는 sessionid 쿠키를 클라이언트에게 보낸다. 이 쿠키는 사용자의 세션을 식별하는 데 사용된다.
        • 또한, CSRF 공격을 방지하기 위해 csrftoken 쿠키도 클라이언트에 보내진다.
  • 로그인 전 → 후

    • 로그인

      {
      "username":"본인 username",
      "password":"본인 pw"
      }



      - 로그인 전

    • 로그인 POST 후
      : 로그인 성공시 { “message”: “success” } 를 출력하려면

      class Login(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(request, username=username, password=password)
              print(user)
      
              if user:
                  login(request, user)
                  return Response({"message":"success"}, status=status.HTTP_200_OK) # 이 부분을 수정
              else:
                  return Response(status=status.HTTP_403_FORBIDDEN)

+) config/settings.py에서 아래 코드 부분을 주석처리 해야 로그인이 유지되서 이후 실습인 로그아웃을 할 수 있다.

# REST_FRAMEWORK = {
#     "DEFAULT_AUTHENTICATION_CLASSES": [
#         "rest_framework.authentication.TokenAuthentication",
#     ],
# }

2. Logout

  • POST 생성

    • users/views.py

      # django의 session을 활용한 로그아웃
      from django.contrib.auth import logout
      
      class Logout(APIView):
      	# login한 user에 대한 확인 필요.
          permission_classes = [IsAuthenticated]
      
          def post(self, request):
              print("request", request.headers)	# header 데이터 체크
              logout(request)
              return Response(status=status.HTTP_200_OK)

      • session id 삭제됨.

3. Login API Test (Postman)

  1. 새 요청 생성
  • Postman에서 "New" 버튼을 클릭하고 "Request"를 선택한다.
  • 요청에 이름을 지정하고(예: "Login Test"), 컬렉션을 생성한 후 저장한다.
  1. 로그인 엔드포인트 설정
  • HTTP 메소드를 "POST"로 설정한다.
  • URL 입력란에 로그인 API의 URL을 입력한다.
    ex) http://localhost:8000/api/v1/users/login
  1. 로그인 데이터 입력
  • "Body" 탭을 선택한다.
  • "raw"를 선택하고, 드롭다운 메뉴에서 "JSON"을 선택한다.
  • 다음과 같이 사용자 이름과 비밀번호를 JSON 형식으로 입력한다.
    {
      "username": "your_username",
      "password": "your_password"
    }
  1. 요청 보내기
  • "Send" 버튼을 클릭하여 요청을 보낸다.
  1. 응답 확인
  • 성공적으로 로그인되면, 서버로부터 200 OK 응답을 받게 된다.

4. Logout API Test (Postman)

  1. 새 요청 생성
  • 이전과 같이 새 요청을 생성한다. 이번에는 "Logout Test"와 같은 이름을 사용할 수 있다.
  1. 로그아웃 엔드포인트 설정
  • HTTP 메소드를 "POST"로 설정한다.
  • URL 입력란에 로그아웃 API의 URL을 입력한다.
    ex) http://localhost:8000/api/v1/users/logout
  1. 요청 보내기
  • 로그아웃 요청에는 본문(body)이 필요하지 않다.
  • "Send" 버튼을 클릭하여 요청을 보낸다.
  1. 응답 확인
  • 성공적으로 로그아웃되면, 서버로부터 200 OK 응답을 받게 된다.

02. Django JWT (JWT)

사용자 정의 JWT 인증 시스템을 구현하는 방법으로, Django Project에서 Custom JWT 기반 인증을 구현하는 방법에 대해서 설명한다.

1단계. 필요한 라이브러리 설치

Django Project에서 JWT를 사용하기 위해 PyJWT 라이브러리가 필요하다. 이를 설치하기 위해 다음 명령어를 사용한다.

> pip install PyJWT # venv
> poetry add PyJWT # poetry

2단계: 장고 설정에 인증 클래스 추가

Django Project의 설정 파일(config/settings.py)에 새로운 인증 클래스를 추가한다. (기존의 인증 방식은 주석 처리)

# config/settings.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "config.authentication.JWTAuthentication"  # 커스텀 JWT 인증 클래스 사용
    ],
}

3단계: 사용자 정의 JWT 인증 클래스 생성

JWTAuthentication 클래스를 생성하여 JWT를 사용한 인증 로직을 구현한다.
이 클래스는 BaseAuthentication을 상속받아 authenticate 메서드를 오버라이드한다.

  • config/authentication.py 파일 생성

    from django.conf import settings
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from users.models import User
    import jwt
    
    class JWTAuthentication(BaseAuthentication):
        def authenticate(self, request):
            token = request.headers.get("jwt-auth")
    
            if not token:
                return None
    
            try:
                decoded = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
                user_id = decoded.get("id")
    
                if not user_id:
                    raise AuthenticationFailed("Invalid Token")
    
                user = User.objects.get(id=user_id)
                return (user, None)
            except jwt.ExpiredSignatureError:
                raise AuthenticationFailed("Token has expired")
            except jwt.DecodeError:
                raise AuthenticationFailed("Error decoding token")
            except User.DoesNotExist:
                raise AuthenticationFailed("User not found")

4단계: JWT 토큰 생성 및 검증

인증 과정에서 사용할 JWT 토큰을 생성하고 검증하는 로직을 구현한다. 토큰은 사용자 로그인 시 생성되어 반환되며, 이후의 요청에서는 해당 토큰을 헤더에 포함하여 서버로 전송한다.

  • users/views.py

    import jwt
    from django.conf import settings
    
    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(request, username=username, password=password)
    
            if user:
                payload = {"id": user.id, "username": user.username}
    
                token = jwt.encode(
                    payload,
                    settings.SECRET_KEY,
                    algorithm="HS256",
                )
    
                return Response({"token": token})
  • users/urls.py

    urlpatterns = [
        path("login/jwt", JWTLogin.as_view())  # jwt login
    ]

코드를 수정한 뒤 Postman에서 테스트를 진행한다.
(URL [POST] : http://127.0.0.1:8000/api/v1/users/login/jwt)

  • Body(raw)에 username과 password를 JSON 형태로 입력해서 넣어준다.

여기서 발급받은 토큰을 user는 어딘가에 저장해야한다. 하지만 쿠키에 저장하면 보안으로 취약하다. 로컬에 있는 암호화 시킨 파일에 저장하면 그나마 괜찮다.

5단계: API 인증 테스트

모든 설정이 완료되었으면, 실제로 API를 호출하여 사용자 정의 JWT 인증 시스템이 정상적으로 작동하는지 테스트한다. API를 호출할 때, HTTP 요청 헤더에 jwt-auth: <jwt_token>을 포함시킨다.

  • 주의사항
    : 예외 처리) 인증 과정에서 다양한 종류의 예외가 발생할 수 있으므로, 적절한 예외 처리가 중요하다.

  • users./views.py

    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.permissions import IsAuthenticated
    from config.authentication import JWTAuthentication
    
    class UserDetailView(APIView):
        authentication_classes = [JWTAuthentication]
        permission_classes = [IsAuthenticated]
    
        def get(self, request):
            user = request.user
            return Response({
                "id": user.id,
                "username": user.username
            })
  • users/urls.py

    urlpatterns = [
            path("login/jwt/info", views.UserDetailView.as_view())
    ]
  • Postman에서 테스트한다.
    (URL [GET] : http://127.0.0.1:8000/api/v1/users/login/jwt/info)

    • Headers 에 jwt-auth | token 값을 입력 후 send
    • 이 후 아래에 정보 뜸.

+) Simple JWT와의 차이점

  1. 수동 토큰 생성
  • 사용자가 제공한 자격 증명을 authenticate 함수로 검증한다.
  • 유효한 자격 증명인 경우, jwt.encode를 사용하여 직접 JWT 토큰을 생성한다.
  • 이 방식은 토큰의 구조와 내용을 완전히 제어할 수 있게 해준다.
  • 별도의 라이브러리(PyJWT)를 사용한다.
  1. django-rest-framework-simplejwt 사용 (Simple JWT)
  • TokenObtainPairView 및 관련 뷰를 사용하여 자격 증명을 검증하고 토큰을 발급한다.

  • 토큰 생성 및 검증 로직이 라이브러리에 내장되어 있어 복잡한 구현을 요구하지 않는다.

  • 액세스 토큰과 리프레시 토큰을 자동으로 관리한다.

  • django-rest-framework-simplejwt 설정을 통해 토큰 동작을 조정할 수 있다.

  • 사용 상황에 따른 선택

    • Simple JWT 사용
      : 토큰의 발급, 갱신, 검증 등의 기능을 손쉽게 구현하고 싶을 때 적합하다. 또한 리프레시 토큰을 사용하여 보안성을 높이고 싶은 경우에도 좋다.
    • 직접 토큰 생성
      : 토큰의 내용이나 구조를 완전히 제어하고 싶거나, 특정 사용 사례에 맞춘 맞춤형 토큰이 필요한 경우에 적합하다.

03. Django JWT (SimpleJWT)

djangorestframework-simplejwt을 활용한 JWT 인증 방법이다.

1. 준비 사항

djangorestframework-simplejwt 라이브러리 설치한다.

> pip install djangorestframework-simplejwt # venv
> poetry add djangorestframework-simplejwt # poetry

2. Project 및 App 설정

  • config/settings.py

    INSTALLED_APPS = [
        ...
        'rest_framework_simplejwt',
        ...
    ]
    
    REST_FRAMEWORK = {
        ...
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication',
            'rest_framework_simplejwt.authentication.JWTAuthentication'
        )
        ...
    }
    • BasicAuthentication은 HTTP 기본 인증을 사용한다.
    • SessionAuthentication은 장고의 세션 프레임워크를 이용한 인증을 사용한다.
    • TokenAuthentication은 토큰 기반 인증을 사용한다.

3. JWT 인증 설정

settings.py에서 Django REST framework의 기본 인증 설정을 JWT 인증으로 변경한다. (파일내 맨 마지막에 코드를 추가해준다.)

  • 설정값 예시

    from datetime import timedelta
    
    SIMPLE_JWT = {
      # 액세스 토큰의 유효 기간을 5분으로 설정한다.
      'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
      
      # 리프레시 토큰의 유효 기간을 1일로 설정한다.
      'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
      
      # 리프레시 토큰을 갱신할 때마다 새 토큰을 생성하지 않도록 설정한다.
      'ROTATE_REFRESH_TOKENS': False,  
      
      # 토큰을 갱신한 후 이전 토큰을 블랙리스트에 추가한다.
      'BLACKLIST_AFTER_ROTATION': True,
      
      # JWT에 사용할 서명 알고리즘으로 HS256을 사용한다.
      'ALGORITHM': 'HS256',
      
      # JWT를 서명하는 데 사용할 키로 Django의 SECRET_KEY를 사용한다.
      'SIGNING_KEY': SECRET_KEY,
      
      # JWT 검증에 사용할 키입니다. HS256 알고리즘에서는 None으로 설정된다.
      'VERIFYING_KEY': None,  
      
      # 인증 헤더의 타입으로 'Bearer'를 사용한다.
      # Authorization: Bearer <token>
      'AUTH_HEADER_TYPES': ('Bearer',),
      
      # 토큰에 포함될 사용자 식별자 필드로 'id'를 사용한다.
      'USER_ID_FIELD': 'id',  
      
      # 토큰 클레임에서 사용자 식별자에 해당하는 키로 'user_id'를 사용한다.
      'USER_ID_CLAIM': 'user_id',  
      
      # 사용할 토큰 클래스로 AccessToken을 사용한다.
      'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),  
      }
    • Bearer

      1. 표준 규약
        : "Bearer" 용어는 OAuth 2.0 표준에서 정의되었으며, 토큰 기반 인증에서 널리 사용된다. 이 표준화된 접근 방식을 사용함으로써, 개발자들은 보편적으로 인정받는 방식을 따를 수 있으며, 다른 시스템과의 호환성을 유지할 수 있다.

      2. 명확한 의미 전달
        : "Bearer"라는 단어는 토큰의 소지자가 해당 자원에 대한 액세스 권한을 가지고 있다는 것을 명확하게 전달한다. 이는 다른 단어들보다 이 목적에 더 적합하다.

      결론적으로, "Bearer"라는 용어는 토큰 기반 인증에서 널리 사용되고 인정받는 표준 용어이기 때문에 사용되고 있다.

  • 자주사용하는 설정값

    SIMPLE_JWT = {
      "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
      "REFRESH_TOKEN_LIFETIME": timedelta(days=14),
      "SIGNING_KEY": "SECRET",
      "ALGORITHM": "HS256",
      "AUTH_HEADER_TYPES": ("Bearer",),
      }
    • ACCESS_TOKEN vs REFRESH_TOKEN

      • 액세스 토큰 (Access Token)

        • 목적: 사용자의 인증 정보를 담고, API 접근 권한을 부여하는 짧은 기간의 토큰이다.
        • 유효 기간: 일반적으로 짧게 설정된다.
          (예: 5분에서 1시간)
        • 보안: 짧은 유효 기간으로 인해, 토큰이 탈취되더라도 공격자가 오랜 시간 동안 사용할 수 없다.
      • 리프레시 토큰 (Refresh Token)

        • 목적: 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급받기 위해 사용하는 긴 기간의 토큰이다.
        • 유효 기간: 일반적으로 길게 설정됩니다 (예: 7일에서 30일). 이 예제에서는 14일로 설정되어 있다.
        • 보안: 비교적 긴 유효 기간을 가지지만, 오직 새로운 액세스 토큰을 발급받는 용도로만 사용된다.
    • 설계 팁

      1. 유효 기간의 균형
      • 액세스 토큰 : 액세스 토큰은 짧은 유효 기간(예: 5분에서 1시간)을 가짐으로써, 탈취되더라도 제한된 시간 동안만 유효하다. 이는 토큰이 노출되었을 경우의 위험을 줄여준다.
      • 리프레시 토큰: 리프레시 토큰은 긴 유효 기간(예: 7일에서 30일)을 가짐으로써, 사용자가 자주 로그인을 반복하는 불편함을 줄여준다. 그러나 이는 동시에 토큰이 탈취될 위험을 증가시킬 수 있다.
      1. 액세스 토큰 만료 시 리프레시
        : 사용자가 계속해서 서비스를 이용하는 동안에는, 액세스 토큰이 만료되어도 자동으로 리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받을 수 있어야 한다. 이를 위해 클라이언트 측 애플리케이션은 액세스 토큰의 만료를 감지하고, 필요 시 리프레시 토큰을 이용해 새 토큰을 요청하는 로직을 구현해야 한다.
      2. 리프레시 토큰의 안전한 저장
        : 리프레시 토큰은 상대적으로 긴 유효 기간을 가지므로, 안전한 저장소에 보관되어야 한다. 예를 들어, 서버 측에서는 데이터베이스에 안전하게 저장하고, 클라이언트 측에서는 적절하게 암호화된 형태로 로컬 저장소에 보관해야 한다.
      3. 리프레시 토큰의 재발급 고려
        : 보안을 강화하기 위해, 리프레시 토큰을 사용할 때마다 새로운 리프레시 토큰을 발급하고, 이전 토큰은 무효화하는 것이 좋다. 이렇게 하면, 리프레시 토큰이 탈취되더라도 탈취자가 오랫동안 사용할 수 없게 된다.
      4. 비정상적 접근 감지
        : 서버는 토큰 사용 패턴을 모니터링하여 비정상적인 접근을 감지해야 한다. 예를 들어, 짧은 시간 내에 여러 국가에서 동일한 토큰으로 요청이 들어오는 경우, 이는 토큰이 탈취되었을 가능성이 있다. 이런 경우에는 즉시 해당 토큰을 무효화하고, 사용자에게 경고를 보내어 추가 조치를 취하도록 해야 한다.
    • ACCESS_TOKEN과 REFRESH_TOKEN을 분리하는 이유

      1. 보안 강화를 위한 분리
        : 액세스 토큰과 리프레시 토큰을 분리함으로써, 두 토큰이 각각 다른 목적으로 사용되고, 이에 따라 다른 보안 수준을 적용할 수 있다. 액세스 토큰은 짧은 유효 기간을 가지므로, 실제 리소스에 접근하는 데 사용되며, 만약 탈취되더라도 짧은 시간 동안만 유효하다. 반면, 리프레시 토큰은 오랜 기간 동안 유효하지만, 오직 새로운 액세스 토큰을 발급받는 데만 사용된다.

      2. 성능과 효율성 향상
        : 액세스 토큰은 상대적으로 짧은 유효 기간을 가지므로, 자주 갱신해야 한다. 이는 서버에 부하를 줄이고, 사용자의 요청 처리 속도를 빠르게 하기 위한 선택이다. 짧은 유효 기간의 액세스 토큰을 사용함으로써, 각 요청에 대한 인증 과정을 빠르게 처리할 수 있다.

      3. 세션 유지의 편의성
        : 리프레시 토큰은 사용자가 자주 로그인을 반복하는 것을 방지한다. 사용자가 서비스를 지속적으로 이용하는 동안에는, 리프레시 토큰을 사용해 자동으로 새로운 액세스 토큰을 발급받을 수 있어, 끊임없는 사용자 경험을 제공할 수 있다.

      4. 토큰 탈취에 대한 대응
        : 액세스 토큰이 탈취되더라도, 그 피해는 짧은 유효 기간으로 인해 제한적이다. 반면, 리프레시 토큰이 탈취되면 보다 심각한 문제가 발생할 수 있으나, 이를 방지하기 위한 여러 보안 조치를 취할 수 있다.
        (예: 안전한 저장, 사용 패턴 모니터링, 재발급 및 무효화 등)

    결국, 액세스 토큰과 리프레시 토큰의 분리는 보안과 성능, 그리고 사용자 경험을 모두 고려한 설계 결정이다. 리프레시 토큰의 보안에 특별한 주의를 기울이면서, 이 두 가지 유형의 토큰을 효과적으로 사용하면, 보다 안전하고 효율적인 인증 시스템 구축을 할 수 있다.

    • REFRESH_TOKEN 탈취되면 보안 끝?

      1. 리프레시 토큰의 안전한 저장
        : 리프레시 토큰은 매우 중요하므로, 클라이언트 측에서는 이를 안전하게 저장하고 관리해야 한다. 예를 들어, 웹 애플리케이션에서는 쿠키에 저장하는 대신, 보안이 강화된 저장소를 사용해야 한다.

      2. 리프레시 토큰의 재발급 및 무효화
        : 리프레시 토큰을 사용할 때마다 새로운 리프레시 토큰을 발급하고, 이전 토큰을 무효화하는 방법을 고려해야 한다. 이렇게 하면, 토큰이 탈취되었다 하더라도, 공격자가 오랜 시간 동안 그 토큰을 사용할 수 없게 된다.

      3. 리프레시 토큰의 사용 패턴 모니터링
        : 서버는 리프레시 토큰의 사용 패턴을 모니터링하여 비정상적인 행동을 감지해야 한다. 예를 들어, 짧은 시간 내에 다수의 액세스 토큰 발급 요청이 발생한다면 이는 의심스러운 행동으로 간주될 수 있다.

      4. 리프레시 토큰의 활성화 구역 제한
        : 가능하다면, 리프레시 토큰의 사용을 특정 지역이나 IP 주소로 제한하는 것도 좋은 방법이다. 이를 통해 무단 접근을 효과적으로 방지할 수 있다.

      5. 두 단계 인증 (Two-Factor Authentication, 2FA)
        : 보안을 더욱 강화하기 위해, 사용자 계정에 대한 두 단계 인증을 도입할 수 있다. 이렇게 하면, 공격자가 리프레시 토큰을 탈취했더라도 추가적인 인증 단계가 있어 무단 접근을 방지할 수 있다.

4. URL 설정

JWT 관련 뷰를 위한 URL을 설정한다.

  • users/urls.py 코드 추가

    from rest_framework_simplejwt.views import (
        TokenObtainPairView,
        TokenRefreshView,
        TokenVerifyView
    )
    
    urlpatterns = [
            # simple JWT
        path("login/simpleJWT", TokenObtainPairView.as_view()),
        path("login/simpleJWT/refresh", TokenRefreshView.as_view()),
        path("login/simpleJWT/verify", TokenVerifyView.as_view())
    ]
    • URL에서 대소문자 구별해서 인식한다.

(1) TokenObtainPairView (/login/simpleJWT)

  • 사용 시점: 사용자가 처음으로 로그인할 때 사용된다.
  • 기능: 사용자의 자격 증명 (사용자 이름과 비밀번호)을 받아서 검증하고, 유효하다면 액세스 토큰과 리프레시 토큰을 발급한다.
  • 프론트엔드 개발자의 역할: 사용자가 로그인 폼을 통해 자격 증명을 제공하면, 이를 이 뷰에 전달하여 토큰 쌍을 받는다.

(2) TokenRefreshView (/login/simpleJWT/refresh)

  • 사용 시점: 액세스 토큰이 만료되었을 때 사용된다.
  • 기능: 유효한 리프레시 토큰을 받아 새로운 액세스 토큰을 발급한다.
  • 프론트엔드 개발자의 역할: 액세스 토큰이 만료되었음을 감지하면, 리프레시 토큰을 이 뷰에 전달하여 새로운 액세스 토큰을 받는다.

(3) TokenVerifyView (/login/simpleJWT/verify)

  • 사용 시점: 토큰의 유효성을 검증할 필요가 있을 때 사용된다.
  • 기능: 제공된 액세스 토큰이 유효한지 검증한다.
  • 프론트엔드 개발자의 역할: 토큰의 유효성을 확인하고 싶을 때 이 뷰를 사용할 수 있다.
  • 위 함수들을 상속 받아 커스텀도 가능하다.
    django-rest-framework-simplejwt 라이브러리는 TokenRefreshViewTokenVerifyView를 이미 제공하므로 이들에 대한 별도의 view 함수를 작성할 필요는 없다. 그러나, 이러한 뷰의 작동 방식을 이해하고 사용자 정의를 하고 싶다면, 기본 뷰를 상속받아 확장할 수 있다.
    다음은 TokenRefreshViewTokenVerifyView를 확장하는 방법을 보여주는 예시다.

    • TokenRefreshView 확장
      : users/views.py에 아래 코드를 추가한다.

      from rest_framework_simplejwt.views import TokenRefreshView
      from rest_framework.response import Response
      
      class CustomTokenRefreshView(TokenRefreshView):
          def post(self, request, *args, kwargs):
              # 여기에서 커스텀 로직을 추가할 수 있습니다.
              # 예를 들어, 추가 로그 작성, 토큰 갱신 전/후 처리 등을 구현할 수 있습니다.
      
              # 부모 클래스의 post 메소드를 호출하여 기본 동작을 수행합니다.
              response = super().post(request, *args, kwargs)
      
              # 추가 응답 데이터나 로직을 여기에 추가할 수 있습니다.
              # 예: response.data['custom_field'] = 'custom_value'
      
              return response
    • TokenVerifyView 확장
      : users/views.py에 아래 코드를 추가한다.

      from rest_framework_simplejwt.views import TokenVerifyView
      from rest_framework.response import Response
      
      class CustomTokenVerifyView(TokenVerifyView):
          def post(self, request, *args, kwargs):
              # 여기에서 커스텀 로직을 추가할 수 있습니다.
              # 예를 들어, 토큰 검증 로그 작성, 추가 검증 로직 등을 구현할 수 있습니다.
      
              # 부모 클래스의 post 메소드를 호출하여 기본 동작을 수행합니다.
              response = super().post(request, *args, kwargs)
      
              # 추가 응답 데이터나 로직을 여기에 추가할 수 있습니다.
              # 예: response.data['custom_message'] = 'Token is valid'
      
              return response
    • 이러한 커스텀 뷰를 사용하려면, Django의 URL 설정에서 기본 뷰 대신 이 커스텀 뷰를 사용해야 한다. (users/urls.py에 코드를 추가한다.)

      from django.urls import path
      from .views import CustomTokenRefreshView, CustomTokenVerifyView
      
      urlpatterns = [
          # ...
          path('login/simpleJWT/refresh', CustomTokenRefreshView.as_view(), name='token_refresh'),
          path('login/simpleJWT/verify', CustomTokenVerifyView.as_view(), name='token_verify'),
      ]

      이 방법으로 django-rest-framework-simplejwt의 기본 동작에 추가적인 로직을 적용할 수 있다.

(4) 프론트엔드에서의 토큰 처리 방식

  • 액세스 토큰 만료 감지
    : 프론트엔드 애플리케이션은 일반적으로 액세스 토큰의 만료를 감지하는 로직을 구현한다. 이는 토큰의 유효기간 정보를 확인하거나, 서버로부터의 응답 상태 코드를 통해 이루어질 수 있다.

  • 리프레시 토큰 사용
    : 액세스 토큰이 만료되면, 프론트엔드는 저장된 리프레시 토큰을 사용하여 TokenRefreshView에 요청을 보내 새로운 액세스 토큰을 받는다.

  • 새로운 액세스 토큰 사용
    : 새로 발급받은 액세스 토큰을 이후의 요청에 사용하여 서비스를 지속적으로 이용한다.

즉, 프론트엔드 개발자는 액세스 토큰이 만료되었을 때, 리프레시 토큰만 서버에 보내 새 액세스 토큰을 받는다. 서버는 리프레시 토큰을 검증하고, 유효하다면 새로운 액세스 토큰을 발급한다.

5. Test 방법

  1. 토큰 생성
  1. 토큰 인증 확인
  • Postman : 유저 데이터
    (URL [GET] : http://127.0.0.1:8000/api/v1/users/login/jwt/info)

    • users/view

      from config.authentication import JWTAuthentication
      class UserDetailView(APIView):
        # authentication_classes = [JWTAuthentication] ## 주석 처리 필요
        permission_classes = [IsAuthenticated]
      
        def get(self, request):
            user = request.user
      
            return Response({"id":user.id, "username":user.username})


  1. TokenRefreshView 테스트
  1. TokenVerifyView 테스트
  • Postman

    {
      "token": "your_access_token_here"
    }
    • 토큰이 유효할 시 → { }

    • token이 유효하지 않을 시 → 아래 오류

      {
      	"detail": "Token is invalid or expired",
      	"code": "token_not_valid"
      }


[4일차 후기]

아직도 인증 주는거에 대해서 완벽하게 이해하진 못했는데,,, 다양한 방법이 있다는 건 알겠다😂


[참고 자료]

  • [오즈스쿨 스타트업 웹 개발 초격차캠프 백엔드 Django 강의]
profile
백엔드 코린이😁

0개의 댓글