如何使用 Django rest 框架来延长 JWT session 令牌?

How do I use the Django rest framework to prolong a JWT session token?

我在 django.auth.contrib 应用程序和 djangorestframework-jwt==1.11.0 中使用 Django 3.2。如何在收到对经过身份验证的资源的请求并验证用户可以访问该资源后 prolong/reissue 一个新的 session 令牌?我使用以下序列化程序和视图来登录用户并发出初始令牌

class UserLoginSerializer(serializers.Serializer):

    username = serializers.CharField(max_length=255)
    password = serializers.CharField(max_length=128, write_only=True)
    token = serializers.CharField(max_length=255, read_only=True)

    def validate(self, data):
        username = data.get("username", None)
        password = data.get("password", None)
        user = authenticate(username=username, password=password)
        if user is None:
            raise serializers.ValidationError(
                'A user with this email and password is not found.'
            )
        try:
            payload = JWT_PAYLOAD_HANDLER(user)
            jwt_token = JWT_ENCODE_HANDLER(payload)
            update_last_login(None, user)
        except User.DoesNotExist:
            raise serializers.ValidationError(
                'User with given email and password does not exists'
            )
        return {
            'username':user.username,
            'token': jwt_token
        }

class UserLoginView(RetrieveAPIView):

    permission_classes = (AllowAny,)
    serializer_class = UserLoginSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        response = {
            'success' : 'True',
            'status code' : status.HTTP_200_OK,
            'message': 'User logged in successfully',
            'token' : serializer.data['token'],
            }
        status_code = status.HTTP_200_OK

        return Response(response, status=status_code)

我在我的设置文件中有这个,最初将 session 保持为 1 小时

JWT_AUTH = {
    # how long the original token is valid for
    'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),

}

客户端在“授权”中提交 session 令牌 header 并使用以下视图对其进行验证(例如)

class UserProfileView(RetrieveAPIView):

    permission_classes = (IsAuthenticated,)
    authentication_class = JSONWebTokenAuthentication

    def get(self, request):
        try:
            token = get_authorization_header(request).decode('utf-8')
            if token is None or token == "null" or token.strip() == "":
                raise exceptions.AuthenticationFailed('Authorization Header or Token is missing on Request Headers')
            decoded = jwt.decode(token, settings.SECRET_KEY)
            username = decoded['username']
            
            status_code = status.HTTP_200_OK
            response = {
                'success': 'true',
                'status code': status_code,
                'message': 'User profile fetched successfully',
                'data': {
                        #...
                    }
                }

        except Exception as e:
            status_code = status.HTTP_400_BAD_REQUEST
            response = {
                'success': 'false',
                'status code': status.HTTP_400_BAD_REQUEST,
                'message': 'User does not exists',
                'error': str(e)
                }
        return Response(response, status=status_code)

我想在我的回复中做的是向用户发送一个新的 session 令牌,该令牌可以再使用一个小时,但我不清楚我需要调用什么来生成这样的令牌and/or edit/invalidate 现有的

首先,我建议选择 djangorestframework-simplejwt over django-rest-framework-jwt(未维护)。

两者的基本观点是:

  • 获取令牌视图(即登录),获取凭据和returns一对访问和刷新令牌
  • 刷新令牌视图,采用有效的刷新令牌和returns刷新的访问令牌

您的令牌将有 2 个不同的生命周期。您的访问令牌通常会存在几分钟,而只要您希望会话有效,您的刷新令牌就会一直有效。

访问令牌用于证明您的身份验证。过期后,由于刷新视图,您应该请求另一个。如果您的刷新令牌无效(已过期或列入黑名单),您可以擦除客户端上的身份验证状态并再次请求凭据以获得新的对。

默认情况下,当您进行身份验证时,您将拥有一个在固定到期前有效的刷新令牌。到达后,即使您处于活动状态,也需要再次进行身份验证。

如果您需要稍微短暂的会话,您可能需要模仿 Django 的 SESSION_SAVE_EVERY_REQUEST to postpone the session's expiry. You can achieve this by rotating refresh tokens: when you request a new token to your refresh view, it will issue both renewed access and refresh tokens, and the refresh one would have its expiry postponed. This is covered by djangorestframework-simplejwt thanks to the ROTATE_REFRESH_TOKENS 设置。

JWT 发布后无法更改,因此您无法延长其生命周期,但您可以这样做:

for every request client makes:
    if JWT is expiring:
       generate a new JWT and add it to the response

客户端将使用这个新发行的令牌。
为此,您可以添加一个 django middleware: **已编辑

class ExtendJWTToResponse:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        
        jwt_token = get_authorization_header(request).decode('utf-8')
        new_jwt_token = None
        try:
            payload = jwt.decode(jwt_token, settings.SECRET_KEY)
            new_jwt_token = JWT_ENCODE_HANDLER(payload)
        except  PyJWTError:
            pass
            
        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.
        if new_jwt_token:
            response['Refresh-Token'] = new_jwt_token

        return response

并且客户端必须检查 'Refresh-Token' header 响应,如果有响应,它应该替换令牌并使用新颁发的具有延长生命周期的令牌。
注意:最好限制新令牌的发行,例如,每次请求令牌将在接下来的 20 分钟内过期...