向 DRF 简单 JWT 负载添加声明

Adding claims to DRF simple JWT payload

使用djangorestframework_simplejwt库,当POST到自定义视图

#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/ 我可以看到有效载荷当前是

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

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

我浏览了 and now would like to know how to customize the Payload data by adding iat claim、用户名和今天的日期。

由于您已经为所需的视图 (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,您可以看到相应的负载变化

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

在你的views.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(Whosebug)kra1*@tqel626wf@&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'"
    }

当我来到这里寻找一种解决方案,在使用 dj-rest-auth and djangorestframework_simplejwt I found a solution in the 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"
}