Django

JSON Web Token, JWT

zhelddustmq 2024. 8. 30. 10:19

Session & Cookie

⭐ 쿠키 (Cookie)

  • 웹 브라우저와 요청과 응답을 주고받을때 사용하는 데이터 조각
  • 쿠키는 도메인에 제한적이며 유효기간이 정해져있음
  • Auth 외에도 다양한 방식으로 활용 (광고...?)

⭐ 세션 (Session)

  • stateless한 HTTP 특징을 보완하기 위한 방법
  • 세션 DB를 이용해서 유저의 정보를 기억하며 Session ID라고 하는 랜덤한 Key를 쿠키에 담아서 Auth에 활용
  • 쿠키를 사용해서 Session ID를 주고 받는 것

 

JSON Web Token, JWT: 쿠키는 웹 브라우저에만 존재. 어플 등 다양한 장치와 소통 필요!

 

  • Token은 간단히 말해 랜덤하게 생긴 문자열이며, 일정한 규칙을 가지고 있고 간단한 서명을 더한 문자열로 토큰 자체에 유저에 대한 간단한 정보가 들어있는 형태
  • JWT 방식으로 Auth를 처리하면 Session DB나 인증을 위한 여러가지 로직 처리가 필요 없음.

처리방식

  1. 클라이언트가 ID/PW를 서버로 보냄
  2. 서버에서 ID/PW를 검증하고 유효하다면 일정한 형식으로 서명 처리된 Token을 응답
  3. 이후 클라이언트는 모든 요청 헤더에 토큰을 담아 서버로 요청을 전송
  4. 서버는 해당 토큰의 유효성을 검증하고 유저의 신원과 권한을 확인 후 요청을 처리

세션과의 차이

  • 세션 데이터베이스가 존재하지 않으며 데이터베이스가 필요x
  • 토큰 자체가 하나의 인증 데이터
  • 서버는 토큰이 유효한지만 검증하여 처리

장점

  • 서버에서 관리하는 데이터가 없으므로 복잡한 처리 로직이 필요x
  • 세션이나 DB없이 유저를 인증하는것이 가능

단점

  • 일방적으로 로그인을 무효화 하는 등의 처리가 불가능 (세션 테이블 없기 때문)
    • 모든 기기 로그아웃
    • 현재 접속 유저 관리 등
  • Token 자체가 데이터를 담고 있는 정보이므로 탈취당할시 보안이 취약

JWT 구조

  • https://jwt.io/
  • eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    -> . 을 기준으로 HEADER . PAYLOAD . VERIFY SIGNATURE 세 부분으로 구성
  • HEADER
    • 토큰의 타입(jwt) 또는 서명 부분의 생성에 어떤 알고리즘(alg)이 사용되었는지 등을 저장
  • PAYLOAD
    • 토근 발급자, 토큰 대상자, 토큰 만료시간, 활성날짜, 발급시간 등등
    • 여러 데이터가 담겨있는 부분
    • 이곳에 서비스가 유저 정보를 담아서 인증
    • 누구나 풀어볼 수 있기 때문에 민감한 정보를 담지않고 최소의 정보만 저장
      • user의 pk 값 등
    • 각각의 데이터는 Claim이라고도 하며 Key-Value 형태로 구성됨
  • SIGNATURE
    • HEADER + PAYLOAD + 서버의 비밀키 값을 HEADER에 명시된 암호 알고리즘 방식으로 생성한 값
    • 즉 PAYLOAD의 글자 하나만 달라져도 SIGNATURE는 완전히 다른 문자열로 변환되어 서버의 비밀키 값을 모른다면 유효한 서명값을 만들어내는 것이 불가능
    • 서버는 토큰을 받으면 HEADER + PAYLOAD + 비밀키로 생성한 서명값이 토큰의 서명값과 일치하는지를 확인하는 과정을 거쳐서 유효성 여부를 확인
    • 서명의 유효여부 + 유효기간 내의 토큰인지 확인하여 Auth 과정을 처리

Access Token과 Refresh Token

  • 토큰이 털리면 큰일남.
    → 유효기간을 짧게 둬버림

➡️ Access Token

  • 요청할 때 인증을 위해 헤더에 포함해야하는 토큰
  • 매 요청시 보내는 토큰이므로 보안이 취약
  • 만료 기한을 짧게 잡아 만약 탈취 당해도 짧은 시간안에 유효하지 않은 토큰 형태

🔃 Refresh Token

  • Access Token이 만료되었을때 새로 Access Token을 발급받기 위한 Token
  • Access Token 보다 긴 유효기간
  • 주로 사용자의 기기에 저장해두고 사용되며 만약 Refresh Token까지 만료되었다면 다시 인증(로그인) 과정이 필요
  • Refresh Token의 탈취를 보완하기 위해 DB 리소스를 사용하는 다양한 방식도 존재합니다. (BlackList 등)

JWT 구현하기

https://django-rest-framework-simplejwt.readthedocs.io/en/latest/index.html

pip install djangorestframework-simplejwt

 

settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
}

accounts/urls.py

from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path("signin/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
    path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]

Login JSON(superuser)

{
	"username": "admin",
  "password" : "admin1234"
}

로그인 이후 토큰 발급

JWT 유효기간 설정(JWT customizing)

from datetime import timedelta
...

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=1),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    "ROTATE_REFRESH_TOKENS": True,
    "BLACKLIST_AFTER_ROTATION": True,
}
from datetime import timedelta
...

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=1), # access token 유효시간 1분
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1), # refresh token 유효시간 하루
    "ROTATE_REFRESH_TOKENS": True, # refresh요청 할때마다 다시 refresh token 줌
    "BLACKLIST_AFTER_ROTATION": True, # 이전 refresh token 블랙리스트 테이블에 올림
}

 

 

03. 접근 제한하기: 로그인 후 이용가능을 JWT로 구현

 

유저가 아니면 Article 기능 접근 제한

articles/views.py

from rest_framework.permissions import IsAuthenticated # 이부분과
...
          
 class ArticleListAPIView(APIView):
	permission_classes = [IsAuthenticated] # 이부분만 추가함

    def get(self, request):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

Token을 이용해서 인증하기

  • Token 정보는 요청 Header에 담아서 전달
  • postman은 보다 편리하게 auth를 담을 수 있도록 편의 기능을 제공

Access Token 만료 또는 잘못된 Token인 경우

Access Token의 유효기간을 1분으로 잡아놓아서 그럼. 이렇게 짧게 설정하는 것이 안전하지만 지금 테스트를 하며 개발하고 있기 때문에 기간을 조금 늘려도 괜찮음.

 

정상적인 Access Token 사용시
실제로는 Header에 담김

  • Bearer
    • JWT 혹은 OAuth에 대한 토큰임을 명시하는 것 (RFC 6750)
    • 이 값도 특정 단어로 변경할 수 있으나 권장하지 않음.(그냥 쓰세요)

'Django' 카테고리의 다른 글

Django ORM 최적화  (0) 2024.08.30
Django ORM (심화)  (0) 2024.08.30
Serializer 활용하기  (0) 2024.08.29
Relationship과 DRF(with. particular seed)  (0) 2024.08.29
DRF CBV  (0) 2024.08.29