일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Django
- 알고리즘 문제
- form
- web
- CSS
- django widget
- 파이썬 알고리즘
- HTML
- django rest framework
- API
- 알고리즘 연습
- Git
- Algorithm
- es6
- AWS
- 백준
- PYTHON
- java
- 파이썬
- MAC
- 알고리즘
- DRF
- react
- js
- c++
- 알고리즘 풀이
- 장고
- javascript
- Baekjoon
- django ORM
- Today
- Total
수학과의 좌충우돌 프로그래밍
[Django] Token 인증 적용하기, TokenAuthentication 본문
DRF 에서 지원하는 인증은 다음과 같습니다.
-
rest_framework.authentication.SessionAuthentication
-
rest_framework.authentication.BasicAuthentication
-
rest_framework.authentication.TokenAuthentication
- 초기에 username/password 으로 Token 발급
- 이 Token을 매 API 요청에 담아서 보내어 인증을 처리
SessionAuthentication
,BasicAuthentication
은 django default 이고 TokenAuthentication
은 따로 설정을 해줘야합니다.
그래서 기본적인 SessionAuthentication
,BasicAuthentication
을 사용하면 좋겠지만 각각 문제점이 있습니다.
외부 서비스 / 앱에서는 세션인증은 사용 할 수 없고 매번 username/password 를 넘기는 것은 위험합니다.
이럴 경우 TokenAuthentication
을 사용해야 하니 이에 대해서 알아보도록 하겠습니다.
기본세팅
TokenAuthentication
을 적용하기 전에 기본적인 django project 를 설정해보겠습니다.
# 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
를 사용하기 위해서는 pillow
를 설치하셔야 합니다.
pip isntall pillow
author
에 username
은 자동으로 저장하기 위해서 ReadOnlyField
를 사용하였습니다.
# 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']
token 인증을 하기 위해서 TokenAuthentication
을 설정해주고, Permissions 은IsAuthenticated
을 설정해주었습니다.
Permissions 과 관련된 내용은 아래 링크를 참고해주세요.
https://ssungkang.tistory.com/entry/Django-Authentication-%EA%B3%BC-Permissions
# 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을 사용했으므로 DefaultRouter
를 사용할 수 있습니다.
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)),
]
httpie
로 request 를 보내보겠습니다.
그 결과, 아래와 같이 인증이 필요하다고 나옵니다.
http localhost:8000/post/
'''
HTTP/1.1 401 Unauthorized
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 58
Content-Type: application/json
Date: Sun, 12 Jan 2020 05:22:54 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept
WWW-Authenticate: Token
X-Frame-Options: SAMEORIGIN
{
"detail": "Authentication credentials were not provided."
}
'''
Token 인증 설정
rest_framework
에서는 authtoken
app 을 제공해주고 있습니다.
# settings.py
INSTALLED_APPS = [
# 생략
'rest_framework.authtoken',
]
이는 Token
이라는 model을 지원하고 있기때문에 migrate 를 해줘야합니다.
python manage.py migrate
Token
model 은 다음과 같은 특징이 있습니다.
User
model 과 1대1 관계- key 값을 저장하는
key
필드와 생성한 날짜 시간을 저장하는created
필드 key
필드는primary_key
이기 때문에 각User
별로 고유- 각 자리수는 16진수이고 최대 길이가 40 이므로
16^40
개
# 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 요청을 보내서 생성합니다.
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 로 노출
config 의 urls.py 에 이를 추가해줍니다.
# config/urls.py
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns += [
path('api-token-auth/', obtain_auth_token),
]
해당 url로 POST 요청을 보낼 시 password를 틀리면 error가 오게 되고
http POST localhost:8000/api-token-auth/ username="user1" password="잘못된 패스워드"
"""
HTTP/1.1 400 Bad Request
Allow: POST, OPTIONS
Content-Length: 68
Content-Type: application/json
Date: Mon, 13 Jan 2020 14:15:35 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Cookie
X-Frame-Options: SAMEORIGIN
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
"""
제대로 된 password로 요청을 보내면 token
이 만들어지게 됩니다.
이 때 계속 되는 요청에 대해 토큰을 계속 생성하지는 않고 이미 만들어진 같은 토큰을 반환합니다.
http POST localhost:8000/api-token-auth/ username="user1" password="정확한 패스워드"
"""
HTTP/1.1 200 OK
Allow: POST, OPTIONS
Content-Length: 52
Content-Type: application/json
Date: Mon, 13 Jan 2020 14:38:45 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Cookie
X-Frame-Options: SAMEORIGIN
{
"token": "158b16d3ccd098c6a8025d29ccec18f994c0aed3"
}
"""
Token 사용하기
post 목록을 요청하기 위해서는 인증이 필요합니다.
인증이 없으면 request 를 보내도 인증이 필요하다고 나옵니다.
http localhost:8000/post/
"""
HTTP/1.1 401 Unauthorized
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 58
Content-Type: application/json
Date: Tue, 14 Jan 2020 12:55:08 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept
WWW-Authenticate: Token
X-Frame-Options: SAMEORIGIN
{
"detail": "Authentication credentials were not provided."
}
"""
이 때 인증 token을 함께 넘겨주면 정상적으로 요청이 넘어옵니다.
token을 넘겨줄 때는 띄어쓰기도 조심해주셔야 합니다.
http localhost:8000/post/ "Authorization: Token 158b16d3ccd098c6a8025d29ccec18f994c0aed3"
"""
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 126
Content-Type: application/json
Date: Tue, 14 Jan 2020 13:07:50 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept
X-Frame-Options: SAMEORIGIN
[
{
"auth_username": "root",
"id": 1,
"message": "root 가 하는 말",
"photo": "http://localhost:8000/post/8ton_logo2_X7kfW29.jpeg"
}
]
"""
마찬가지로 글을 작성할 때도 위와 같이 token을 넘겨줘서 작성할 수 있습니다.
http POST localhost:8000/post/ "Authorization: Token 158b16d3ccd098c6a8025d29ccec18f994c0aed3" message="hello"
"""
HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 62
Content-Type: application/json
Date: Tue, 14 Jan 2020 13:37:02 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept
X-Frame-Options: SAMEORIGIN
{
"auth_username": "root",
"id": 2,
"message": "hello",
"photo": null
}
"""
image를 같이 업로드 하기 위해서는 다음과 같이 httpie 문법을 사용하여 넘겨줄 수 있습니다.
이 때는 form 으로만 넘겨야 합니다.
http --form POST localhost:8000/post/ "Authorization: Token 158b16d3ccd098c6a8025d29ccec18f994c0aed3" message="hello" photo@'django_image.jpg'
"""
HTTP/1.1 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 103
Content-Type: application/json
Date: Tue, 14 Jan 2020 13:40:50 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept
X-Frame-Options: SAMEORIGIN
{
"auth_username": "root",
"id": 3,
"message": "hello",
"photo": "http://localhost:8000/post/django_image.jpg"
}
"""
'웹프로그래밍 > DRF' 카테고리의 다른 글
[Django] APIView에 permission 지정하기 (1) | 2020.02.26 |
---|---|
[Django] CORS, Cross-Origin Resource Sharing (0) | 2020.02.14 |
[Django] rest_framework default 설정 (0) | 2020.01.08 |
[Django] Throttling (0) | 2020.01.08 |
[Django] Authentication 과 Permissions (0) | 2020.01.08 |