【问题标题】:How do I use the Django rest framework to prolong a JWT session token?如何使用 Django rest 框架来延长 JWT 会话令牌?
【发布时间】:2021-11-29 20:30:15
【问题描述】:

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

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)

我的设置文件中有此设置,以将会话保持在最初 1 小时

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

}

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

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)

我想在回复中做的是向用户发送一个新的会话令牌,该令牌可以再使用一个小时,但我不清楚我需要拨打什么电话来生成这样的令牌和/或编辑/无效现有的。

【问题讨论】:

    标签: django django-rest-framework jwt django-authentication django-contrib


    【解决方案1】:

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

    两者基本上都有这些观点:

    • 获取令牌视图(即登录),获取凭据并返回一对访问和刷新令牌
    • 刷新令牌视图,获取一个有效的刷新令牌并返回一个刷新后的访问令牌

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

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

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

    如果您需要一个短暂的会话,您可能想要模仿 Django 的 SESSION_SAVE_EVERY_REQUEST 来推迟会话的到期。您可以通过轮换刷新令牌来实现这一点:当您向刷新视图请求新令牌时,它将发出更新的访问令牌和刷新令牌,并且刷新令牌的到期时间将被推迟。由于ROTATE_REFRESH_TOKENS 设置,这由djangorestframework-simplejwt 覆盖。

    【讨论】:

    • 感谢您提供有关已弃用库的说明。关于“一旦达到,即使您处于活动状态,您也需要再次进行身份验证”,这不是我想要的。我希望会话在一小时不活动后过期。如果用户继续向有效端点发出请求,我希望无需登录即可继续会话。我不希望在每个请求的“授权”标头中提交两个令牌,但我想如果绝对无法实现,我会开放。
    【解决方案2】:

    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”标头,如果有的话,它应该替换令牌并使用新发行的具有延长生命周期的令牌。
    注意:最好限制发布新令牌,例如,每次请求令牌将在接下来的 20 分钟内到期...

    【讨论】:

    • 是我要编写的“get_token_from_reques”和“check_jwt_is_valid”函数还是由 Django REST 框架提供的?
    • @Dave 它们是隐喻,您必须编写它们,对于“get_token_from_request”,您之前使用过“get_authorization_header”,对于“check_jwt_is_valid”,您使用过“jwt.decode”
    • 我已经编辑了答案,所以它现在使用真正的功能:)))
    猜你喜欢
    • 2018-11-19
    • 2016-06-27
    • 2014-04-14
    • 2017-01-25
    • 2019-10-31
    • 2018-12-15
    • 2021-08-06
    • 2018-05-04
    • 2016-06-23
    相关资源
    最近更新 更多