#python #django #django-rest-framework #jwt
#python #django #django-rest-framework #jwt
Вопрос:
пытаясь выяснить, почему я получаю недопустимую ошибку токена, приведенную в моем коде ниже. Я тестирую регистрацию и аутентификацию через свой API.
Я создаю фиктивную учетную запись, а затем проверяю свою электронную почту на наличие ссылки для подтверждения. Все работает отлично, пока я не нажимаю на ссылку в электронном письме и не получаю 400 ошибочных запросов, и из-за моей отладки ошибка вызвана «Недопустимым токеном».
Вот мой код:
views.py
import jwt
from django.urls import reverse
from django.contrib.sites.shortcuts import get_current_site
from django.conf import settings
#from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework import generics ,status
from rest_framework.response import Response
from rest_framework import views
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.permissions import AllowAny
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from .models import NewUser
from .serializers import RegisterSerializer, EmailVerificationSerializer, LoginSerializer
from .utils import ConfirmEmail
class CustomUserCreate(generics.GenericAPIView):
permission_classes = [AllowAny]
serializer_class = RegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
user = NewUser.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
relativeLink = reverse('users:email-verify')
absurl = 'http://' current_site relativeLink "?token=" str(token)
email_body = 'Hi ' user.username
' Use the link below to verify your email n' absurl
data = {'email_body': email_body, 'to_email': user.email,
'email_subject': 'Verify Your Email'}
ConfirmEmail.send_email(data)
return Response(user_data, status=status.HTTP_201_CREATED)
class VerifyEmail(views.APIView):
permission_classes = [AllowAny]
serializer_class = EmailVerificationSerializer
token_param_config = openapi.Parameter(
'token', in_=openapi.IN_QUERY, description='Description', type=openapi.TYPE_STRING)
@swagger_auto_schema(manual_parameters=[token_param_config])
def get(self, request):
token = request.GET.get('token')
try:
payload = jwt.decode(token, settings.SECRET_KEY).decode("utf-8")
user = NewUser.objects.get(id = payload['user_id'])
if not user.is_verified:
user.is_verified = True
user.save()
return Response({'email': 'Successfully Activated'}, status=status.HTTP_200_OK)
except jwt.ExpiredSignatureError as identifier:
return Response({'error': 'Activation Expired'}, status=status.HTTP_400_BAD_REQUEST)
except jwt.exceptions.DecodeError as identifier:
return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
class LoginAPIView(generics.GenericAPIView):
permission_classes = [AllowAny]
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
serializers
from django.contrib import auth
from rest_framework import serializers
from rest_framework.exceptions import AuthenticationFailed
from users.models import NewUser
class RegisterSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
username = serializers.CharField(required=True)
password = serializers.CharField(min_length=8, max_length=68, write_only=True)
class Meta:
model = NewUser
fields = ('email', 'username', 'password','first_name','last_name')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
class EmailVerificationSerializer(serializers.ModelSerializer):
token = serializers.CharField(max_length=555)
class Meta:
model = NewUser
fields = ['token']
class LoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=255, min_length=3)
password = serializers.CharField(max_length=68, min_length=6, write_only=True)
username = serializers.CharField(max_length=255, min_length=3, read_only=True)
tokens = serializers.SerializerMethodField()
def get_tokens(self, obj):
user = NewUser.objects.get(email=obj['email'])
return {
'refresh': user.tokens()['refresh'],
'access': user.tokens()['access']
}
class Meta:
model = NewUser
fields = ['email', 'password', 'username', 'tokens']
def validate(self, attrs):
email = attrs.get('email', '')
password = attrs.get('password', '')
filtered_user_by_email = NewUser.objects.filter(email=email)
user = auth.authenticate(email=email, password=password)
if not user:
raise AuthenticationFailed('Invalid credentials, try again')
if not user.is_active:
raise AuthenticationFailed('Account disabled, contact admin')
if not user.is_verified:
raise AuthenticationFailed('Email is not verified')
return {
'email': user.email,
'username': user.username,
'tokens': user.tokens()
}
return super().validate(attrs)
models
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from rest_framework_simplejwt.tokens import RefreshToken
class NewUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=150, unique=True, db_index=True, default=None)
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(max_length=150, blank=True)
last_name = models.CharField(max_length=150, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
#newsletter = models.BooleanField(default=False)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = CustomAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
def __str__(self):
return self.email
def tokens(self):
refresh = RefreshToken.for_user(self)
return {
'refresh': str(refresh),
'access': str(refresh.access_token)
}
Не уверен, в чем проблема, я пытался отключить прослушку в своей степени, но теперь я уперся в стену. Заранее благодарю вас.
РЕДАКТИРОВАТЬ: вот мой файл настроек
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=10),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('JWT',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
}
проект urls.py
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
path('admin/', admin.site.urls),
path('api/', include('bucket_api.urls', namespace='bucket_api')),
path('auth/', include('users.urls')),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('', include('bucket.urls', namespace='bucket')),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
приложение urls.py
:
urlpatterns = [
path('register/', CustomUserCreate.as_view(), name="register"),
path('login/', LoginAPIView.as_view(), name="login"),
path('email-verify/', VerifyEmail.as_view(), name="email-verify"),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
#path('logout/blacklist/', BlacklistTokenUpdateView.as_view(), name='blacklist'),
]
согласно jwt.io , мой токен должен быть действительным…
Ответ №1:
Итак, мне наконец-то удалось устранить ошибку. Прежде всего, когда вы отлаживаете, это поможет прокомментировать, exceptions
чтобы просмотреть фактические ошибки. Я должен был это знать, но я был упрям в своих методах…
В любом случае, вы должны указать тип алгоритма, в котором был сгенерирован JWT при использовании JWT.decode
. Как вы видите, вот моя новая полезная нагрузка с добавленным HS256
алгоритмом. payload = jwt.decode(token, settings.SECRET_KEY, algorithms='HS256')
Надеюсь, это поможет вам в будущем!
Ответ №2:
вы можете использовать django rest framework JWT следующим образом
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
JWT_AUTH = {
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_PAYLOAD_GET_USER_ID_HANDLER':
'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
'JWT_SECRET_KEY': settings.SECRET_KEY,
'JWT_GET_USER_SECRET_KEY': None,
'JWT_PUBLIC_KEY': None,
'JWT_PRIVATE_KEY': None,
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_LEEWAY': 0,
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': False,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
'JWT_AUTH_HEADER_PREFIX': 'JWT',
'JWT_AUTH_COOKIE': None,
}
urls.py
from rest_framework_jwt.views import obtain_jwt_token
from rest_framework_jwt.views import refresh_jwt_token
from rest_framework_jwt.views import verify_jwt_token
from django.urls import path
#...
urlpatterns = [
'',
# ...
path('api-token-auth/', obtain_jwt_token),
path('api-token-refresh/', refresh_jwt_token),
path('api-token-verify/', verify_jwt_token),
]
Комментарии:
1. Привет, спасибо за помощь, я на самом деле использую
rest_framework_simplejwt
, не смог заставить ваш код работать. Я также настроил свои настройки JWT в своем коде, пожалуйста, смотрите Обновленный пост для получения новой информации.