가입된 사용자 별로 API 키를 발급하고 해당 API 키를 이용하여 인증하는 REST API 구현해 보자.
https://www.django-rest-framework.org
https://florimondmanca.github.io/djangorestframework-api-key/
Installation
Django REST Framwork와 Django REST Framework API Key를 설치한다.
(env) $ pip install djangorestframework
(env) $ pip install djangorestframework-api-key
Django 프로젝트 설정
Django project와 application을 생성하고, settings.py 파일에 아래 내용을 추가한다.
...
# Application definition
INSTALLED_APPS = [
...
"rest_framework",
"rest_framework_api_key",
...
]
...
# Restframework
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework_api_key.permissions.HasAPIKey",
]
}
DEFAULT_PERMISSION_CLASSES를 설정해 두면, REST Framework로 구성된 API의 기본 permission이 된다.
구현
Views
API Key로 인증하는 API를 만들어 보자. views.py 파일에 다음과 같이 추가한다.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def health(request):
return Response({'message': 'OK'}, status=status.HTTP_200_OK)
앞서 설정 파일에서 DEFAULT_PERMISSION_CLASSES 를 HasAPIKey 로 설정해 두었기 때문에, 모든 API는 따로 Permission class를 설정하지 않는 한, API Key 인증을 시도하게 된다.
만약 DEFAULT_PERMISSION_CLASSES 를 설정하지 않고, 개별 API 마다 설정하고 싶다면, 다음과 같이 추가할 수 있다.
...
from rest_framework.decorators import permission_classes
from rest_framework_api_key.permissions import HasAPIKey
@api_view(['GET'])
@permission_classes([HasAPIKey])
def health(request):
return Response({'message': 'OK'}, status=status.HTTP_200_OK)
다음으로, API Key를 조회하고, 발급하는 API를 만들어 보자. views.py 파일에 다음과 같이 추가한다.
...
from datetime import datetime, timedelta
from rest_framework_api_key.models import APIKey
from rest_framework.permissions import IsAuthenticated
@api_view(['POST', 'GET'])
@permission_classes([IsAuthenticated])
def apikey(request):
username = request.user.username
if request.method == 'GET':
response = {}
keys = APIKey.objects.all()
for key in keys:
if key.name == username:
response['apikey'] = key.prefix + '*****'
response['created'] = key.created
response['expires'] = key.expiry_date
break
return Response(response, status=status.HTTP_200_OK)
else:
keys = APIKey.objects.all()
for key in keys:
if key.name == username:
key.delete()
break
api_key, key = APIKey.objects.create_key(name=request.user.username)
expires = datetime.now() + timedelta(days=90)
api_key.expiry_date = expires
api_key.save()
return Response({'apikey': key, 'expires': expires}, status=status.HTTP_200_OK)
Permission class는 IsAuthenticated 로 설정하여, 사용자 ID와 비밀번호를 통한 인증을 수행한다.
GET 요청이 올 경우, API Key 들 중에 사용자 이름을 갖는 키의 정보를 전달한다.
POST 요청이 올 경우, 기존에 사용자 이름을 갖는 키가 있을 경우, 이를 삭제하고 사용자 이름을 이름으로 갖는 키를 생성한다. 해당 키는 90일 뒤에 expire되도록 설정하고, 생성된 키 값과 함께 expire 정보를 전달한다.
사용자 정의 API Key 모델을 사용하여 User 와 API Key 1:1 관계를 정의해야 할 것 같음. → 아래 Customize에서 진행하도록 하자.
URLs
만들어진 뷰를 URL로 연동해 보자. urls.py 파일에 다음과 같이 작성한다.
from django.urls import path
from . import views
app_name = 'api'
urlpatterns = [
path('health', views.health),
path('apikey', views.apikey),
]
테스트
우선 API Key 없이 health API를 호출해 보자.
$ curl -X GET 127.0.0.1:8000/api/health
아래와 같이 Django REST framework에서 발생시키는 오류 결과를 확인할 수 있다.
{
"detail": "자격 인증데이터(authentication credentials)가 제공되지 않았습니다."
}
API Key를 발급해 보자. (API Key 값을 발급 당시에만 확인할 수 있으며, 추후에는 확인이 불가능하니, 적절한 곳에 잘 기록해 둬야 한다.)
$ curl -X POST --user 사용자아이디:비밀번호 127.0.0.1:8000/api/apikey
아래와 같이 apikey 값과 만료일자가 수신됨을 확인할 수 있다.
{
"key":"hgHg2XK7...",
"expires":"2021-09-21T14:32:26.015103"
}
이제 발급된 키 값을 이용하여 다시 health API를 호출해 보자.
$ curl -X GET \
--header "Authorization: Api-Key hgHg2XK7..." \
127.0.0.1:8000/api/health
아래와 같이 API Key를 이용한 인증이 수행되고, 정상적으로 결과값이 수신됨을 확인할 수 있다.
{
"message": "OK"
}
Customize APIKey Model
API Key키를 User Model과 OneToOneField를 이용하여 관계를 지어 보자. 한 명의 사용자에게 하나의 API Key만 발급될 수 있도록!!
Models
models.py 파일에 AbstractAPIKey 를 상속한 Custom한 API Key 모델을 생성한다.
from django.db import models
from django.contrib.auth import get_user_model
from rest_framework_api_key.models import AbstractAPIKey
class UserAPIKey(AbstractAPIKey):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name="apikey")
class Meta(AbstractAPIKey.Meta):
ordering = ['user']
verbose_name = "User API key"
django.contrib.auth에서 제공하는 get_user_model helper를 이용하여 User Model을 가져온다.
related_name을 이용하여 User 모델을 통해서 API Key model에 접근할 수 있도록 한다.
model이 변경되었으므로, makemigrations 명령을 이용하여 migration을 해야 한다.
Permissions
permission에 사용되는 rest_framework_api_key.permissions의 HasAPIKey 는 built-in APIKey에만 동작한다. 즉, 사용자 정의 API Key Model을 만들어 사용하는 경우, Permission class 또한 생성하여 API Key를 검증해야 한다. permissions.py 파일을 생성하고, BaseHasAPIKey를 상속받아 Permission class를 구현한다.
from rest_framework_api_key.permissions import BaseHasAPIKey
from .models import UserAPIKey
class HasUserAPIKey(BaseHasAPIKey):
model = UserAPIKey
다음, 해당 Permission class를 사용하도록 views.py 파일을 수정한다.
...
from .permissions import HasUserAPIKey
@api_view(['GET'])
@permission_classes([HasUserAPIKey])
def health(request):
return Response({'message': 'OK'}, status=status.HTTP_200_OK)
Views
views.py 파일을 수정하여 rest_framework_api_key.models의 APIKey가 아닌 앞서 만든 UserAPIKey 모델을 사용하도록 한다.
...
from .models import UserAPIKey
...
@api_view(['POST', 'GET'])
@permission_classes([IsAuthenticated])
def apikey(request):
user = request.user
if request.method == 'GET':
if hasattr(user, 'apikey'):
apikey = user.apikey
return Response({
'prefix': apikey.prefix,
'created': apikey.created,
'expires': apikey.expiry_date
}, status=status.HTTP_200_OK)
return Response({}, status=status.HTTP_204_NO_CONTENT)
else:
if hasattr(user, 'apikey'):
user.apikey.delete()
api_key, key = UserAPIKey.objects.create_key(name=request.user.username, user=user)
expires = datetime.now() + timedelta(days=90)
api_key.expiry_date = expires
api_key.save()
return Response({'apikey': key, 'expires': expires}, status=status.HTTP_200_OK)
hasattr 을 이용하여 User와 연결된 API Key가 있는지 확인한다. 키 생성 요청의 경우, 이미 해당 User에게 키가 존재할 경우, 삭제하고 생성한다.
Admin
관리자 페이지에서 키 관리를 할 수 있도록 추가해 줄 수도 있다. admin.py 파일에 다음과 같이 추가한다.
from django.contrib import admin
from rest_framework_api_key.admin import APIKeyModelAdmin
from rest_framework_api_key.models import APIKey
from .models import UserAPIKey
admin.site.unregister(APIKey)
@admin.register(UserAPIKey)
class UserAPIKeyModelAdmin(APIKeyModelAdmin):
pass
기존에 default로 사용되는 APIKey는 삭제하고, 생성한 UserAPIKey를 등록한다.
'일하는 > Cloud, Web' 카테고리의 다른 글
[Testing][웹 서버 성능 테스트 도구] Wrk (1) | 2021.11.11 |
---|---|
[Testing][웹 서버 성능 테스트 도구] Apache Bench (0) | 2021.11.11 |
[AWS] EC2 인스턴스에서 CodeCommit 사용 설정하기 (0) | 2021.06.07 |
[AWS] AWS CodeCommit (0) | 2021.06.07 |
Node.js (0) | 2021.06.03 |