Django DRF: 외부 서비스를 위한 API Key 인증 방식 구현
외부 서비스나 서드파티 클라이언트가 안전하게 API를 호출할 수 있도록 하는 API Key 기반 인증 시스템 구현

API Key 인증이 필요한 이유
JWT나 세션 인증은 주로 사용자 신원 확인용
서버 대 서버 통신에서는 API Key가 적합
파트너사 서버, 외부 모니터링 시스템, 모바일 앱 백엔드 등에서 사용
APIKey 모델 설계
class APIKey(models.Model):
prefix = models.CharField(max_length=8, unique=True, editable=False)
hashed_key = models.CharField(max_length=128, editable=False)
service = models.CharField(max_length=100, help_text="키를 사용하는 서비스 이름")
created = models.DateTimeField(auto_now_add=True, db_index=True)
is_active = models.BooleanField(default=True)
def set_key(self, key: str):
from django.contrib.auth.hashers import make_password
self.hashed_key = make_password(key)
def check_key(self, key: str) -> bool:
from django.contrib.auth.hashers import check_password
return check_password(key, self.hashed_key)핵심 포인트:
API Key는 평문 저장 금지, 해시값으로 저장
prefix와 key를 분리하여 관리
is_active 필드로 소프트 삭제 구현
커스텀 인증 클래스 구현
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class APIKeyAuthentication(BaseAuthentication):
keyword = 'Api-Key'
def authenticate(self, request):
auth_header = request.headers.get('Authorization')
if not auth_header:
return None
try:
keyword, key = auth_header.split()
except ValueError:
return None
if keyword.lower() != self.keyword.lower():
return None
api_key = self._authenticate_credentials(key)
return (None, api_key)
def _authenticate_credentials(self, key: str):
try:
prefix, secret_key = key.split('.')
except ValueError:
raise AuthenticationFailed("잘못된 API Key 형식")
try:
api_key = APIKey.objects.get(prefix=prefix, is_active=True)
except APIKey.DoesNotExist:
raise AuthenticationFailed("유효하지 않은 API Key")
if not api_key.check_key(secret_key):
raise AuthenticationFailed("유효하지 않은 API Key")
return api_key권한 클래스 구현
from rest_framework.permissions import BasePermission
class HasAPIKey(BasePermission):
message = "유효한 API Key가 필요함"
def has_permission(self, request, view):
return isinstance(request.auth, APIKey)View에 적용
from rest_framework.views import APIView
from rest_framework.response import Response
class ProtectedDataView(APIView):
authentication_classes = [APIKeyAuthentication]
permission_classes = [HasAPIKey]
def get(self, request):
service_name = request.auth.service
return Response({
"message": f"'{service_name}' 서비스 접근 성공",
"data": ["보호된 데이터"]
})실무 고려사항
HTTPS 필수: API Key는 평문으로 전송되므로 반드시 HTTPS 사용
키 교체 기능: 주기적 키 재발급 및 이전 키 비활성화 기능 구현
세분화된 권한: API Key별로 접근 가능한 범위를 다르게 설정
여러 인증 방식 동시 지원: JWT, 세션 인증과 함께 사용 가능
Last updated
