【问题标题】:(Django Simple JWT) 401 Unauthorized when logging out(Django Simple JWT) 注销时 401 Unauthorized
【发布时间】:2022-12-22 12:41:26
【问题描述】:

当我尝试注销时,我在使用简单的 JWT 时遇到了问题。我被告知注销时,必须删除 cookie 并且必须将刷新令牌列入黑名单!

除注销外,所有先前的身份验证步骤(注册、登录)都按预期工作。

登记

登录

登出

在上面的捕获中,我得到了 401 unauthorized。 cookie 似乎已按预期删除。

正如我发现的 Q/As 所建议的那样,我尝试了以下方法:

  • 打印异常,但除了 401 错误之外什么也没有显示。
  • 添加授权标头和“Bearer”与“JWT”之间的互换。
  • 修改settings.py,应用简单的jwt认证码模板。我的代码如下:

设置.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
    
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUTH_HEADER_TYPES': ('Bearer', 'JWT',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'email',
    'USER_ID_CLAIM': 'user_id',
    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
}

AUTH_USER_MODEL = 'users.CustomUser'

CORS_ALLOWED_ORIGINS = [
    "http://127.0.0.1:3000",
    "http://localhost:3000",
    "http://127.0.0.1:8000",
    "http://localhost:8000",
]

序列化器.py

class LogoutSerializer(serializers.Serializer):
    refresh = serializers.CharField()

    default_error_messages = {
        'bad_token': _('Token is invalid or expired')
    }

    def validate(self, attrs):
        self.token = attrs['refresh']
        return attrs

    def save(self, **kwargs):
        try:
            RefreshToken(self.token).blacklist()
        except TokenError:
            self.fail('bad_token')
        except Exception as e:
            print('\nException in logging out:', e)

视图.py

class RegisterView(APIView):
    permission_classes = [AllowAny]
    def post(self, request, format='json'):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            user = serializer.save()
            if user:
                json = serializer.data
                return Response(json, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class LoginView(APIView):
    def post(self, request):
        email = request.data['email']
        password = request.data['password']

        user = CustomUser.objects.filter(email=email).first()

        if user is None:
            raise AuthenticationFailed('User not found!')

        if not user.check_password(password):
            raise AuthenticationFailed('Incorrect password!')

        payload = {
            # 'id': user.id,
            'email': user.email,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
            'iat': datetime.datetime.utcnow()
        }

        token = jwt.encode(payload, 'secret', algorithm='HS256')

        response = Response({'message': 'success'})

        response.set_cookie(key='jwt', value=token, httponly=True)
        
        
        response.data = {
            'jwt': token
        }
        return response


class LogoutView(generics.GenericAPIView):
    serializer_class = LogoutSerializer
    permission_classes = (permissions.IsAuthenticated,)
    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(status=status.HTTP_204_NO_CONTENT)   

用户/urls.py

urlpatterns = [
    path('', UserList.as_view()),
    path('register', RegisterView.as_view()),
    path('login', LoginView.as_view()),
    path('user', UserView.as_view()),
    path('logout', LogoutView.as_view()),
]

模型.py

class CustomUserManager(BaseUserManager):
    def create_superuser(self, email, password=None):
        if password is None:
            raise TypeError('Password should not be none')
        user = self.create_user(email, password)
        user.is_superuser = True
        user.is_staff = True
        if user.is_superuser is not True:
            raise ValueError(
                'Superuser must be assigned to is_staff=True.')
        if user.is_staff is not True:
            raise ValueError(
                'Superuser must be assigned to is_superuser=True.')
        user.save()
        return user
    
    
    def create_user(self, email, password=None):
        if email is None:
            raise TypeError('Users should have a Email')
        email = self.normalize_email(email)
        user = self.model(email=email)
        user.set_password(password)
        user.save()
        return user


AUTH_PROVIDERS = {'facebook': 'facebook', 'google': 'google',
                  'twitter': 'twitter', 'email': 'email'}
class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True, db_index=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    about = models.TextField(_(
        'about'), max_length=500, blank=True)
    auth_provider = models.CharField(
        max_length=255, blank=False,
        null=False, default=AUTH_PROVIDERS.get('email'))
    USERNAME_FIELD = 'email'
    # REQUIRED_FIELDS = ['username']

    objects = CustomUserManager()

    def __str__(self):
        return self.email

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

上面的代码来自几个教程,但不知何故没有像他们应该的那样工作! 你能建议我解决这个问题的方法吗?谢谢!

【问题讨论】:

  • 你能解决你的问题吗?还有其他问题,为什么在 LoginView 中使用 'user = CustomUser.objects.filter' 而不是 django.contrib.auth.authenticate()?如果您使用的是ModelBackend,这最后会检查密码。
  • @Jony_23 是的,我在后端解决了这个问题。 CustomUser 模型有问题
  • 那太棒了!我知道,我在自定义用户模型方面遇到了很多问题。
  • 你做了什么来解决这个问题?

标签: django-rest-framework django-rest-framework-simplejwt


【解决方案1】:

这是因为您在 LogoutView 中有 permission_classes = (permissions.IsAuthenticated,)

您可以安全地允许任何用户注销。

permission_classes = (AllowAny,) 

【讨论】:

    猜你喜欢
    • 2015-03-13
    • 2018-08-06
    • 2021-06-17
    • 2021-03-09
    • 2019-02-25
    • 2021-10-11
    • 2019-06-20
    • 1970-01-01
    • 2018-02-04
    相关资源
    最近更新 更多