JWT (JSON Web Token) 인증 방식
토큰 자체에 유저 정보를 담아 별도의 인증 저장소(DB 등) 없이 인증 가능
Cookie는 브라우저에 국한되므로 다양한 환경에서 통합 인증이 어려움 → JWT방식의 Token 인증이 사용됨
토큰 자체가 하나의 인증 데이터
- 클라이언트가 ID/PW 전송
- 서버는 검증 후 JWT 발급
- 클라이언트는 JWT를 헤더에 담아 서버에 요청
- 서버는 토큰 유효성 확인 후 요청 처리
*Cookie(쿠키)
웹 브라우저와 요청과 응답을 주고받을 때 사용하는 데이터 조각
쿠키는 도메인에 제한적이며 유효기간이 정해져있음
Auth 외에도 다양한 방식으로 활용
**Session(세션)
stateless한 HTTP 특징을 보완하기 위한 방법
세션 DB를 이용해서 유저의 정보를 기억하며 Session ID라고 하는 랜덤한 Key를 쿠키에 담아서 Auth에 활용
쿠키를 사용해서 Session ID를 주고 받는 것
***Token(토큰)
랜덤하게 생긴 문자열
일정한 규칙을 가지고 있고 간단한 서명을 더한 문자열로 토큰 자체에 유저에 대해 간단한 정보가 들어있는 형태
JWT 구성
. 을 기준으로 HEADER, PAYLOAD, VERIFY SIGNATURE 세 부분으로 구성됨
- Header: 토큰의 타입(jwt) 또는 서명 부분의 생성에 사용된 알고리즘 정보 (alg) 등
- Payload: user pk, 발급 시간 등 사용자 정보 (Claim들), Key-Value 형태로 구성됨
→ 누구나 디코딩 가능하기 때문에 민감한 정보는 Payload에 담으면 안됨 - Signature: Header + Payload를 서버 비밀키로 암호화한 서명
→ Payload의 한 글 자만 달라져도 Signature는 완전히 다른 문자열로 변환되어 서버의 비밀키 값을 모른다면 유효한 서명값을 만들어내는 것이 불가능
→ 서버는 토큰을 받으면 Header + Payload + 비밀키로 생성한 서명값이 토큰의 서명값과 일치하는지를 확인하는 과정을 거쳐서 유효성 여부를 확인 → 서명의 유효여부 + 유효기간 내의 토큰인지 확인하여 Auth 과정을 처리
Custom User Model 만들기
accounts 앱 생성 후 User 모델 작성
python manage.py startapp accounts
# accounts/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
# settings.py
INSTALLED_APPS= [
...,
# Local
"accounts,"
]
AUTH_USER_MODEL = "accounts.User"
URL 연결 설정
# 프로젝트의 urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/articles/", include("articles.urls")),
path("api/v1/accounts/", include("accounts.urls")),
]
DB 초기화
rm db.sqlite3
# 모든 마이그레이션 파일 삭제 (init은 남겨야 함)
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc" -delete
Superuser 생성
python manage.py createsuperuser
데이터 시드 (Seeding) 작업
Article 모델에 50개 랜덤 데이터 삽입
python manage.py seed articles --number=50
Article(id=1)에 연결된 Comment 20개 생성
python manage.py seed articles --number=20 --seeder "Comment.article_id" 1
JWT 설정 및 적용
라이브러리 설치
pip install djangorestframework-simplejwt
인증 설정
# settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
}
토큰 발급 URL 설정
# accounts/urls.py
from django.urls import path
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"),
]
JWT 유효기간 설정
# settings.py
from datetime import timedelta
INSTALLED_APPS = [
...,
# Third-party
"rest_framework_simplejwt.token_blacklist",
...,
]
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), # 테스트 시에는 30분 추천
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
}
python manage.py migrate
- Access Token
요청할 때 인증을 위해 헤더에 포함해야하는 토큰
매 요청시 보내는 토큰이므로 보안이 취약함
만료 기한을 짧게 잡아 탈취 당하더라도 짧은 시간안에 유효하지 않은 토큰 형태로 만듦 - Refresh Token
Access Token이 만료되었을 때 새로 Access Token을 발급받기 위한 Token
Access Token보다 긴 유효시간을 가짐
주로 사용자의 기기에 저장해두고 사용되며 Refresh Token까지 만료되었다면 다시 인증과정(로그인) 필요
Refresh Token의 탈취를 보완하기 위해 DB 리소스 사용하는 다양한 방식 존재 (BlackList 등)
접근 제한 설정
로그인한 사용자만 접근 가능한 뷰
# 예: articles/views.py
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer
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)
토큰을 사용한 인증
클라이언트는 JWT를 헤더에 담아 요청
Bearer
JWT 혹은 OAuth에 대한 토큰임을 명시하는것(RFC 6750 – The OAuth 2.0 Authorization Framework: Bearer Token Usage)
이 값도 특정 단어로 변경할 수 있으나 권장되지 않음
유효기간이 짧게 설정되었을 경우, 토큰이 만료되면 401 Unauthorized 응답 발생
만료된 경우 token/refresh/ 엔드포인트를 통해 재발급 가능
request.user 를 통해 유저 정보 확인
class ArticleListAPIView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
print("\n현재 유저의 유저네임: ", request.user.username, "\n\n")
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)
토큰이 유효한 경우, 뷰 내부에서 request.user 를 통해 현재 로그인한 유저 정보를 바로 사용할 수 있음
'∟ Framework > ∟ DRF' 카테고리의 다른 글
ORM 최적화 (0) | 2025.03.31 |
---|---|
Django ORM(Object Relational Mapping) 활용 (0) | 2025.03.31 |
DRF Serializer 활용 (1) | 2025.03.30 |
DRF models with relationship CRUD API 구현 (0) | 2025.03.30 |
Class Based View (CBV) (0) | 2025.03.29 |