【问题标题】:Adding claims to DRF simple JWT payload向 DRF 简单 JWT 有效负载添加声明
【发布时间】:2020-09-18 15:41:42
【问题描述】:

在 POST 到自定义视图时使用 djangorestframework_simplejwt

#urls.py
path('api/token/', MyTokenObtainPairView.as_view(), name='token_obtain'),

#views.py
class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

我可以获得以下访问令牌

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkwOTEwNjg0LCJqdGkiOiI3M2MxYmZkOWNmMGY0ZjI3OTY4MGY0ZjhlYjA1NDQ5NyIsInVzZXJfaWQiOjExfQ.5vs0LmNGseU6rtq3vuQyApupxhQM3FBAoKAq8MUukIBOOYfDAV9guuCVEYDoGgK6rdPSIq2mvcSxkILG8OH5LQ

转到https://jwt.io/我可以看到payload当前是

{
  "token_type": "access",
  "exp": 1590910684,
  "jti": "73c1bfd9cf0f4f279680f4f8eb054497",
  "user_id": 11
}

因此,我们可以看到令牌的第二部分是有效负载 - 包含声明。

我探索了how to add more information to the Response body,现在想知道如何通过添加iat claim、用户名和今天的日期来自定义有效负载数据。

【问题讨论】:

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


    【解决方案1】:

    当我来到这里寻找解决方案以在使用 dj-rest-auth 和 djangorestframework_simplejwt 时将令牌过期添加到响应中时,我在 code 中找到了解决方案(显然没有记录?):

    有一个名为 JWT_AUTH_RETURN_EXPIRATION 的设置,如果设置为 True,则会将过期时间添加到响应中,如下所示:

    {
        "access_token": "ACCESS_TOKEN_STRING",
        "refresh_token": "REFRESH_TOKEN_STRING",
        "user": {
            "pk": PK,
            "email": "USER_EMAIL"
        },
        "access_token_expiration": "2021-02-10T10:40:46.883715Z",
        "refresh_token_expiration": "2021-02-11T10:35:46.883728Z"
    }
    

    刷新也是一样的:

    {
        "access": "ACCESS_TOKEN_STRING",
        "access_token_expiration": "2021-02-10T10:47:57.325545Z"
    }
    

    【讨论】:

      【解决方案2】:

      在你看来.py

      from rest_framework_simplejwt.tokens import RefreshToken
      from rest_framework.response import Response
      from rest_framework.decorators import api_view, permission_classes
      from rest_framework.permissions import AllowAny
      from decouple import config
      from django.contrib.auth import authenticate
      import jwt
      
      
      
      
      @api_view(['POST'])
      @permission_classes([AllowAny])
      def get_tokens_for_user(request):
      
          username = request.POST.get("username")
          password = request.POST.get("password")
      
          user = authenticate(username=username, password=password);
      
          if user is not None:
      
              refreshToken = RefreshToken.for_user(user)
              accessToken = refreshToken.access_token
      
              decodeJTW = jwt.decode(str(accessToken), config('SECRET_KEY'), algorithms=["HS256"]);
      
              # add payload here!!
              decodeJTW['iat'] = '1590917498'
              decodeJTW['user'] = 'tiago'
              decodeJTW['date'] = '2020-05-31'
      
              #encode
              encoded = jwt.encode(decodeJTW, config('SECRET_KEY'), algorithm="HS256")
          
              return Response({
                  'status': True,
                  'refresh': str(refreshToken),
                  'access': str(encoded),
              })
      
          else:
              return Response({
                  'status': False
              })
              # No backend authenticated the credentials
      

      在您的 urls.py 中

      from django.urls import path, include
      from .views import get_tokens_for_user
      
      urlpatterns = [
      
              path('login/', get_tokens_for_user, name="login"),
      ]
      

      在您的 settings.py 中

      from pathlib import Path
      from datetime import timedelta
      from decouple import config
      
      ...
      # SECURITY WARNING: keep the secret key used in production secret!
      SECRET_KEY = config('SECRET_KEY')
      
      # Application definition
      
      INSTALLED_APPS = [
      ...
          # Rest
          'rest_framework',
      ...
      ]
      
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': [
              'rest_framework_simplejwt.authentication.JWTAuthentication',
          ],
          'DEFAULT_PERMISSION_CLASSES': [
              'rest_framework.permissions.IsAuthenticated',
          ]
      
      }
      
      # JWT
      # https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html
      SIMPLE_JWT = {
          'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
          'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
          'AUTH_HEADER_TYPES': ('Bearer',),
          'SIGNING_KEY': config('SECRET_KEY'),
          'VERIFYING_KEY': config('SECRET_KEY'),
          'ALGORITHM': 'HS256',
      }
      

      在你的根目录中添加 .env

      SECRET_KEY = 'ep@4ojr4m!h73y2j(stackoverflow)kra1*@tq$5el626wf@&p60)7u!6552+-'
      

      运行时值

       decodeJTW =   {
          'token_type': 'access',
          'exp': 1612651527,
          'jti': '7f415b28610348468ce74ec0f480fad1',
          'user_id': 2,
          'iat': '1590917498',
          'user': 'tiago',
          'date': '2020-05-31'
      }
      
      encode = {
         "status":true,
          "refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYxMjczNDU0NywianRpIjoiMDQ0MDI3ZTQzMTc2NDFiNDhhOGI2MjU4MjE4ZGZjNDkiLCJ1c2VyX2lkIjoyfQ.Qf0YfJLAmdYuavDHVng7Bwjmka551G6c1Gi4e-UdRuc",
      "access":"b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjEyNjUxNzQ3LCJqdGkiOiI2OWEzNjYwYjYxMTk0MzVjYjljZTA0OGQ3MmE1ODk1YSIsInVzZXJfaWQiOjIsImlhdCI6IjE1OTA5MTc0OTgiLCJ1c2VyIjoidGlhZ28iLCJkYXRlIjoiMjAyMC0wNS0zMSJ9.XUMvhL13zDZdbjYYPkYnwlZoHN6U7Zc3xUzXsKoVj2I'"
          }
      

      【讨论】:

        【解决方案3】:

        由于您已经为所需的视图 (MyTokenObtainPairView) 创建了一个子类,并为其对应的序列化器 (MyTokenObtainPairSerializer) 创建了一个子类,因此将以下内容添加到序列化器中

        class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
        
            ...
        
            @classmethod
            def get_token(cls, user):
                token = super().get_token(user)
        
                # Add custom claims
                token['iat'] = datetime.datetime.now()
                token['user'] = user.username
                token['date'] = str(datetime.date.today())
        
                return token
        

        然后,当您 POST 到同一位置时,您将获得这样的访问令牌

        eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkwOTE0MTk4LCJqdGkiOiJhZDZmNzZhZjFmOGU0ZWJlOGI2Y2Y5YjQ4MGQzZjY2MiIsInVzZXJfaWQiOjExLCJpYXQiOjE1OTA5MTc0OTgsInVzZXIiOiJ0aWFnbyIsImRhdGUiOiIyMDIwLTA1LTMxIn0.-5U9P-WWmhlOenzCvc6b7_71Tz17LyNxe_DOMwwqH4RqrNsilVukEcZWFRGupLHRZjIvPya2QJGpiju9ujzQuw
        

        使用 JWT,您可以看到 Payload 相应地发生变化

        {
          "token_type": "access",
          "exp": 1590914198,
          "jti": "ad6f76af1f8e4ebe8b6cf9b480d3f662",
          "user_id": 11,
          "iat": 1590917498,
          "user": "tiago",
          "date": "2020-05-31"
        }
        

        【讨论】:

        • 我相信令牌发行时间戳的正确来源是token.current_time,而不是datetime.datetime.now()token.current_time 在创建 Token 实例时计算,然后用于派生其他数据,例如到期时间。稍后使用datetime.now() 可能会提供与最初计算的时间戳不同的时间戳。
        • @DavidM。你可能是对的,是的。这里的目标只是添加新的声明,它的价值无关紧要。
        • @Tiago,如果我想连同刷新和访问令牌一起发送图像?你可能知道如何做到这一点
        • 有没有办法给payload添加IP地址??
        • @FreddCha 是的,你几乎可以添加任何你想要的东西
        猜你喜欢
        • 2020-08-15
        • 1970-01-01
        • 2016-05-02
        • 2018-08-07
        • 2016-03-12
        • 2021-07-09
        • 2020-12-26
        • 2020-08-26
        • 1970-01-01
        相关资源
        最近更新 更多