DRF에서 지원하는 인증 방식으로는 앞에서 살펴봤듯이 아래와 같이 3가지가 있다.
1. rest_framework.authentication.SessionAuthentication
2. rest_framework.authentication.BasicAuthentication
3. rest_framework.authentication.TokenAuthentication
세션인증은 외부 서비스/앱에서는 사용할 수 없다는 단점이 있고, Basic인증은 외부 서비스/앱에서 매번 username/password를 넘겨야하기 때문에 보안상 위험하다는 단점이 있다.
그러므로 우리는 초기에 username/password로 Token을 발급받고, 이 token을 매 API 요청에 담아서 보내 인증을 처리하는 Token방식에 대해 공부해본다.
TokenAuthentication를 적용하기 전에 기본적인 프로젝트 세팅을 해준다.
# models.py
from django.db import models
from django.conf import settings
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+', on_delete=models.CASCADE)
message = models.TextField(blank=True)
photo = models.ImageField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
이미지를 업로드하는 ImageField를 사용하기 위해서는 pip install pillow
를 통해 pillow를 설치해줘야 한다.
# serializers.py
from rest_framework.serializers import ModelSerializer, ReadOnlyField
from .models import Post
class PostSerializer(ModelSerializer):
auth_username = ReadOnlyField(source='author.username')
class Meta:
model = Post
fields = ['id', 'auth_username', 'message', 'photo']
'auth_username'은 자동으로 저장하기 위해 ReadOnlyField를 사용한다.
# views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from .models import Post
from .serializers import PostSerializer
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
일단 해당 ViewSet에 대해서만 Token인증을 사용하기 위해 authentication_classes에 설정해주고, permission_classes또한 IsAuthenticated로 설정한다.
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register('post', PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]
# settings.py
INSTALLED_APPS = [
# 생략
'rest_framework.authtoken',
]
마지막으로 Token인증을 사용하기 위해서는 rest_framework에서 제공하는 'authtoken' app을 INSTALLED_APPS에 추가해줘야 한다. 또한, 이는 Token
이라는 모델을 지원하고 있기때문에 migrate를 해준다.
Token
모델은 다음과 같은 특징이 있다.
- User
model과 1대1 Relation => OneToOneField
- Token 값을 저장하는 key
필드와 생성한 날짜/시간을 저장하는 created
필드 존재
- 각 User별로 Token 인스턴스가 자동생성되지 않는다
- Token은 primary_key이기 때문에 유저 별로 Unique
# rest_framework/authtoken/models.py
class Token(models.Model):
"""
The default authorization token model.
"""
key = models.CharField(_("Key"), max_length=40, primary_key=True)
user = models.OneToOneField(
settings.AUTH_USER_MODEL, related_name='auth_token',
on_delete=models.CASCADE, verbose_name=_("User")
)
created = models.DateTimeField(_("Created"), auto_now_add=True)
token을 생성하기 위해서는 ObtainAuthToken
APIView에 POST 요청을 보내면 된다. 이 경우에 전에 발급된 token이 있다면 획득, 없다면 새로운 token 생성을 진행한다.
# rest_framework/authtoken/views.py
class ObtainAuthToken(APIView):
# 생략
def post(self, request, *args, **kwargs):
# 생략
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
웹 브라우저로 Token 획득을 보고싶다면 urls.py에 이를 추가해준다.
# urls.py
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns += [
path('api-token-auth/', obtain_auth_token),
]