【问题标题】:Django REST: How do i return SimpleJWT access and refresh tokens as HttpOnly cookies with custom claims?Django REST:如何将 SimpleJWT 访问和刷新令牌作为带有自定义声明的 HttpOnly cookie 返回?
【发布时间】:2021-05-17 17:50:41
【问题描述】:

我想通过HttpOnly cookie 发送SimpleJWT accessrefresh 令牌。我已经定制了索赔。我在 MyObtainTokenPairView(TokenObtainPairView) 中定义了一个 post() 方法,我在其中设置了 cookie。这是我的代码:

from .models import CustomUser

class MyObtainTokenPairView(TokenObtainPairView):
    permission_classes = (permissions.AllowAny,)
    serializer_class = MyTokenObtainPairSerializer
    
    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class()
        response = Response()
        tokens = serializer.get_token(CustomUser)
        access = tokens.access
        response.set_cookie('token', access, httponly=True)
        return response   

返回此错误:

AttributeError: 'RefreshToken' object has no attribute 'access'

序列化器:

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    
    @classmethod
    def get_token(cls, user):
        print(type(user))
        token = super().get_token(user)
        token['email'] = user.email
        return token

但它只是不工作。我想我不应该像这样在这里定义post() 方法。我想如果我只能在序列化程序中返回get_token()函数的值,我可以将它设置为HttpOnly cookie。但是,我不知道该怎么做。

如何在HttpOnly cookie 中设置accessrefresh 标记?

编辑: 我在 anowlinorbit 的回答之后进行了这些更改:

我将我的序列化程序更改为:

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    
    def validate(self, attrs):
        attrs = super().validate(attrs)
        token = self.get_token(self.user)
        token["email"] = self.user.email
        return token

由于此令牌默认包含刷新令牌,因此我决定仅返回此 token 将同时提供 accessrefresh 令牌。 如果我添加像token["access"] = str(token.access_token) 这样的东西,它只会在它已经包含的刷新令牌字符串中添加访问令牌字符串。

但再次在视图中,我找不到如何获取刷新令牌。我无法使用serializer.validated_data.get('refresh', None) 获取它,因为现在我从包含所有内容的序列化程序返回token

我改变了看法:

class MyObtainTokenPairView(TokenObtainPairView):
    permission_classes = (permissions.AllowAny,)
    serializer_class = MyTokenObtainPairSerializer
    
    def post(self, request, *args, **kwargs):
        response = super().post(request, *args, **kwargs)
        response.set_cookie('token', token, httponly=True)
        return response

现在它说:

NameError: name 'token' is not defined

这里有什么问题?在视图中,我想获取从序列化程序返回的token,然后使用token.access_token 获取访问令牌并将refreshaccess 设置为cookie。

【问题讨论】:

    标签: django cookies django-rest-framework django-rest-framework-simplejwt cookie-httponly


    【解决方案1】:

    我将不理会.get_token(),而是专注于.validate()。在您的 MyTokenObtainPairSerializer 中,我将删除您对 .get_token() 的更改并添加以下内容

    def validate(self, attrs):
        data = super().validate(attrs)
        refresh = self.get_token(self.user)
        data["refresh"] = str(refresh)   # comment out if you don't want this
        data["access"] = str(refresh.access_token)
        data["email"] = self.user.email
    
        """ Add extra responses here should you wish
        data["userid"] = self.user.id
        data["my_favourite_bird"] = "Jack Snipe"
        """
        return data
    

    通过使用.validate() 方法,您可以选择希望从序列化程序对象的validated_data 属性返回的数据。 注意我还在序列化程序返回的数据中包含了刷新令牌。同时拥有刷新和访问令牌很重要。如果用户没有刷新令牌,他们将不得不在访问令牌过期时再次登录。刷新令牌使他们无需再次登录即可获得新的访问令牌。

    如果出于某种原因您不想要刷新令牌,请将其从您的 validate() 序列化程序方法中删除并相应地调整视图。

    在这个 post 方法中,我们验证序列化程序并访问其验证数据。

    def post(self, request, *args, **kwargs):
        # you need to instantiate the serializer with the request data
        serializer = self.serializer(data=request.data)
        # you must call .is_valid() before accessing validated_data
        serializer.is_valid(raise_exception=True)  
    
        # get access and refresh tokens to do what you like with
        access = serializer.validated_data.get("access", None)
        refresh = serializer.validated_data.get("refresh", None)
        email = serializer.validated_data.get("email", None)
    
        # build your response and set cookie
        if access is not None:
            response = Response({"access": access, "refresh": refresh, "email": email}, status=200)
            response.set_cookie('token', access, httponly=True)
            response.set_cookie('refresh', refresh, httponly=True)
            response.set_cookie('email', email, httponly=True)
            return response
    
        return Response({"Error": "Something went wrong", status=400)
    

    如果您不想要刷新令牌,您将删除以 refresh = 开头的行并删除添加 refresh cookie 的行。

    【讨论】:

    • 请看似编辑,我被困在view
    • 但是令牌声明中遗漏了email
    • 我用电子邮件更新了代码。您将看到一个模式:首先,在序列化程序中定义数据,使用 serializer.validated_data.get() 获取数据,然后根据需要使用获取的数据。
    • @anowlinorbit,是否可以将图片与 acces_token + 刷新令牌一起发回?我正在使用 DRF + SIMPLE JWT。
    猜你喜欢
    • 2021-05-18
    • 2023-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    • 2020-08-09
    • 2019-02-01
    • 1970-01-01
    相关资源
    最近更新 更多