문제 상황
"rest_framework_jwt"를 사용 시 다음과 같은 오류 발생
Could not import 'rest_framework_jwt.authentication.JSONWebTokenAuthentication' for API setting 'DEFAULT_AUTHENTICATION_CLASSES'.
ImportError: cannot import name 'smart_text' from 'django.utils.encoding'
해당 글을 통해 장고 버전이 4 이상으로 바뀌어서 발생한 것으로 보임
rest_framework_jwt를 임포트할 수 없다는 에러가 발생합니다 - 인프런 | 질문 & 답변
안녕하세요? 강의 잘 듣고 있습니다.강의 11분경 즈음 rest_framework_jwt에러가 발생합니다가상환경에 알맞게 설치되었는지는 확인했고 / 혹시나 해서 pip uninstall후 다시 install까지 해봣는데도 에러
www.inflearn.com
내 장고 버전 또한 4 이상이었기에 발생한 결과이다.
해결 방법
3가지 방법이 있다.
- rest_framework_jwt가 아닌 rest_framework_simplejwt 패키지를 사용한다.
- drf-jwt 패키지를 사용한다.
- django 버전을 3 이하로 낮춘다.
하지만 rest_framework_simplejwt를 사용하면 세부 설정이 다르다길래 나는 더 익숙한 drf-jwt를 사용하기로 하였다.
(추가 : 토큰을 hearder에 담을 때, 접두사로 "jwt "를 사용하는 것이 아니라 "Bearer "를 사용해야 한다)
https://styria-digital.github.io/django-rest-framework-jwt/
Django REST framework JWT
REST framework JWT Auth JSON Web Token Authentication support for Django REST Framework Overview This package provides JSON Web Token Authentication support for Django REST framework. If you want to know more about JWT, check out the following resources: R
styria-digital.github.io
1. 패키지 설치
pip install drf-jwt
2. INSTALLED_APPS에 앱 추가(settings.py)
INSTALLED_APPS = [
...
"rest_framework_jwt",
"rest_framework_jwt.blacklist",
...
]
3. settings.py에 설정 추가
jwt 토큰에 대한 설정은 자신의 입맛대로 하면 된다.
나는 테스트를 위해 최대한 간소화하였다.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
# 'rest_framework.permissions.AllowAny',
),
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
],
}
JWT_AUTH = {
'JWT_SECRET_KEY': 'SECRET_KEY',
'JWT_ALGORITHM': 'HS256',
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': timedelta(days=1),
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=1),
}
4. obtain_jwt_token으로 토큰을 불러온다.(urls.py)
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path("jwt-test/", obtain_jwt_token),
]
결과
성공 결과
실패 결과 1 (비밀번호가 틀린 경우)
실패 결과 2 (존재하지 않는 id일 경우)
틀렸을 때 경우는 똑같이 "제공된 인증 데이터로는 로그인할 수 없습니다" 라는 문구로 반환된다.
직접 토큰 불러오기
성공 시에는 pk, token을, 실패 시에는 non_field_errors를 가져온다.
나는 이렇게 반환되는 것이 불편해서 일관성을 위해 직접 토큰을 가져오기로 했다.
이 내용도 위 jwt 문서에 "Creating a new token manually" 파트에 나와있다.
1. LoginSerializer에서 토큰 가져오기
from rest_framework import serializers
from django.contrib.auth import authenticate
from django.contrib.auth.models import update_last_login
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class LoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=5)
password = serializers.CharField(max_length=32)
def validate(self, data):
username = data.get("username", None)
password = data.get("password", None)
user = authenticate(username=username, password=password)
if user is None:
return {'message': 'fail'}
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
update_last_login(None, user)
return {'message': 'success', 'token': token}
2. view.py에 정의
from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.permissions import AllowAny
from users.models import User
from users.serializers import LoginSerializer
class LoginAPI(APIView):
permission_classes = [AllowAny]
def post(self, request):
serializer = LoginSerializer(data=request.POST)
serializer.is_valid()
if serializer.validated_data['message'] == 'fail':
return JsonResponse(data=serializer.validated_data, status=400)
if serializer.validated_data['message'] == 'success':
return JsonResponse(data=serializer.validated_data, status=200)
# return render(request, "users/login.html", context)
이때 permission_classes = [AllowAny]를 선언해야 한다.
선언을 안 해주면 "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." 401 에러가 발생한다.
결과
성공 결과
실패 결과
사실 어느 순간부터 "CSRF Faild: CSRF token missing or incorrect" 403 에러가 발생해서 이거 해결하려고 1시간동안 고군분투하였다.
미들웨어에서 csrf를 검사하지 못 하도록 주석 처리하거나, @csrf_exempt 어노테이션도 써봤지만 전혀 해결이 되지 않았다.
결국 혼자 부들부들대며 밥 먹으러 갔다오니 갑자기 해결이 되어 있었다.
왜 그런 것인지 아직도 이해가 잘 되지 않는다.
(업데이트: 알고 보니 장고 어드민 사이트를 켜놔서 그렇더라....talend api tester로 요청을 보낼 때에는 장고 어드민 사이트를 꺼놔야 한다. 참고하자..!)
'이슈 > Django' 카테고리의 다른 글
[Django] No such table 에러 + column 생성이 안 될 때 (0) | 2023.12.09 |
---|