[DRF] Token 인증

Django REST framework의 Token Authentication에 대해 공부한다.

Posted by Seoyoung Lee on August 22, 2020 · 5 mins read

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방식에 대해 공부해본다.

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를 해준다.


authtoken 앱에서 지원하는 Token모델

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 생성 방법

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 획득을 API endpoint로 노출

웹 브라우저로 Token 획득을 보고싶다면 urls.py에 이를 추가해준다.

# urls.py

from rest_framework.authtoken.views import obtain_auth_token

urlpatterns += [
  path('api-token-auth/', obtain_auth_token),
]