简体   繁体   English

如何解决 DRF 简单 jwt 用户登录中的错误 401 未授权登录

[英]How to solve error 401 unauthorized login in DRF simple jwt user login

I am creating DRF authentication APIs for Abstract Base users in my Django project and I am using simple JWT.我正在我的 Django 项目中为 Abstract Base 用户创建 DRF 身份验证 API,并且我正在使用简单的 JWT。 The registration and email verification APIs work fine, but when I try to log in using the credentials of a valid user, I get an error 401 unauthorized access.注册和 email 验证 API 工作正常,但是当我尝试使用有效用户的凭据登录时,我收到错误 401 未授权访问。

the custom user model in models.py models.py中的自定义用户 model

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=255, unique=True,db_index=True)
    email = models.EmailField(max_length=255, unique=True,db_index=True)
    is_verified = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELD = ['username']

    objects = UserManager()

    def __str__(self):
        return self.email

    def tokens(self):
        refresh = RefreshToken.for_user(self)
        return{
            'refresh':str(refresh),
            'access': str(refresh.access_token)
        }

Here is my views.py :这是我的views.py

class RegisterView(generics.GenericAPIView):

    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 = User.objects.get(email=user_data['email'])

        token = RefreshToken.for_user(user).access_token

        current_site = get_current_site(request).domain
        relativeLink = reverse('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'}
        Util.send_email(data)

 
        return Response(user_data, status=status.HTTP_201_CREATED)

class VerifyEmail(views.APIView):
    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, algorithms=['HS256'])
            user = User.objects.get(id=payload['user_id'])
            if not user.is_verified:
                user.is_verified = True
                user.save()
            return Response({'email': 'Succesfully 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):
    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)

Here is my serializers.py这是我的serializers.py

class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        max_length=68, min_length=8, write_only=True)
  

    class Meta:
        model = User
        fields= ['email', 'username','password']

    def validate(self, attrs):
        email = attrs.get('email', '')
        username = attrs.get('username', '')
  
        if not username.isalnum():
            raise serializers.ValidationError(
                'The username should only contain alphanumeric charachters')
        return attrs

    def create(self, validated_data):
        return User.objects.create_user(**validated_data) 

class EmailVerificationSerializer(serializers.ModelSerializer):
    token = serializers.CharField(max_length= 555)

    class Meta:
        model = User
        fields = ['token']

class LoginSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(max_length=255, min_length=3)
    password = serializers.CharField(max_length=68, min_length=8, write_only=True)
    username = serializers.CharField(max_length=255, min_length=3, read_only = True)
    tokens = serializers.CharField(max_length=68, min_length=8, read_only = True)


    class Meta:
        model = User
        fields = ['email', 'password', 'username', 'tokens']


    def validate(self, attrs):
        email = attrs.get('email', '')
        password = attrs.get('password', '')
  
        user = auth.authenticate(email=email, password=password)

        if not user:
            raise AuthenticationFailed('Invalid Credentials, try again!')
        if not user.is_active:
            raise AuthenticationFailed('Acccount disabled, please 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)

So the error raised is "Invalid credentials" meaning that details of the user don't exist, while the users are actually there when I check the database.所以引发的错误是“无效的凭据”,这意味着用户的详细信息不存在,而当我检查数据库时用户实际上就在那里。 在此处输入图像描述

Anyone, please help.任何人,请帮助。

What you want is to exclude your login view from the project-wide authentication check that you have added in settings.py.您想要的是从您在 settings.py 中添加的项目范围的身份验证检查中排除您的登录视图。

class LoginAPIView(generics.GenericAPIView):
    permission_classes = ()
    authentication_classes = ()
    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)

So after a lot of googling and headaches, I ended up reading the Simple Jwt documentation again and as it turns out, the 401 error occurs if the user is not active.因此,经过大量的谷歌搜索和头痛之后,我最终再次阅读了Simple Jwt 文档,事实证明,如果用户不活动,则会出现 401 错误。 In my models.py above, by default, my user's is_verified and is_active are False .在我上面的models.py中,默认情况下,我的用户的is_verifiedis_activeFalse The VerifyEmail view changed the is_verified to True after the users verified their emails but their is_active remained False hence giving the error 401 .在用户验证他们的电子邮件后, VerifyEmail视图将is_verified更改为True ,但他们的is_active仍然为False ,因此出现错误 401 My solution was adding an is_active=True for when the user verifies their email: Here is the VerifyEmail view in views.py我的解决方案是在用户验证其 email 时添加is_active=True :这是views.py中的VerifyEmail视图

class VerifyEmail(views.APIView):
    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, algorithms=['HS256'])
            user = User.objects.get(id=payload['user_id'])
            if not user.is_verified:
                user.is_verified = True
                user.is_active = True           # New
                user.save()
            return Response({'email': 'Succesfully 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)

This worked for me.这对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM