JWT, Session, API Key 인증을 한 번에 지원하기

현대적인 서비스는 웹 브라우저, 모바일 앱, 외부 서비스 등 다양한 클라이언트를 지원해야 하므로, 각 환경에 최적화된 여러 인증 방식을 동시에 제공하는 것이 필수

여러 인증 방식이 필요한 이유

웹 브라우저 (SPA): Session 인증이나 JWT 방식 사용 모바일 앱: 서버 부담이 적고 확장성이 좋은 JWT 방식 선호 외부 서비스: 서버 간 통신을 위한 고정된 API Key 인증 사용

DRF 인증 처리 방식

DRF는 authentication_classes에 설정된 인증 클래스들을 순서대로 실행하여 사용자 식별

  • 첫 번째 클래스부터 순차적으로 인증 시도

  • 하나의 인증 클래스가 성공하면 뒤 순서 클래스들은 실행되지 않음

  • 인증 성공 시 request.userrequest.auth 속성 설정

  • 모두 실패하면 request.userAnonymousUser로 설정

구현 방법

API Key 인증 클래스 구현

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import APIKey

class APIKeyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        api_key_str = request.headers.get('X-API-KEY')
        if not api_key_str:
            return None
            
        try:
            api_key = APIKey.objects.get(key=api_key_str, is_active=True)
            user = api_key.user
        except APIKey.DoesNotExist:
            raise AuthenticationFailed('Invalid API Key.')
            
        return (user, api_key)

View에 인증 클래스 설정

from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
from rest_framework_simplejwt.authentication import JWTAuthentication
from .authentication import APIKeyAuthentication

class CombinedAuthView(APIView):
    authentication_classes = [SessionAuthentication, JWTAuthentication, APIKeyAuthentication]
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        return Response({
            'message': f'Hello, {request.user.username}!',
            'auth_method': request.auth.__class__.__name__
        })

인증 순서의 중요성

순서
장점
주의사항

[Session, JWT, APIKey]

웹 브라우저 호환성 최우선, CSRF 보호 정상 동작

JWT 토큰이 있어도 Session이 우선 처리됨

[JWT, Session, APIKey]

Stateless API 우선, 성능 유리

CSRF 보안 취약점 발생 가능

실무 권장사항

엔드포인트 목적별 분리

  • 웹 프론트엔드용: [SessionAuthentication, JWTAuthentication]

  • 모바일/외부 서비스용: [JWTAuthentication, APIKeyAuthentication]

성능 최적화

  • 불필요한 인증 클래스 제거

  • API Key 인증에 캐싱 적용 고려

동적 인증 클래스 변경

def get_authenticators(self):
    if self.request.method == 'GET':
        return [APIKeyAuthentication()]
    return [JWTAuthentication(), SessionAuthentication()]

Last updated