给DRF simple JWT负载添加声明

10
使用 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
}

JWT

所以,我们可以看到token的第二部分是有效载荷 - 包含声明。
我已经探索了如何向响应体添加更多信息,并且现在想知道如何通过添加iat声明、用户名和今天的日期来自定义有效载荷数据。
3个回答

15

既然您已经为所需视图(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,您可以看到有效载荷相应地更改。

Custom Payload simpleJWT Django

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

1
我认为令牌发行时间戳的正确来源是 token.current_time 而不是 datetime.datetime.now()token.current_time 是在创建 Token 实例时计算的,然后用于推导其他数据,例如到期时间。稍后使用 datetime.now() 可能会提供与最初计算的时间戳不同的时间戳。 - David M.
1
@DavidM,你可能是正确的。这里的目标只是添加新的声明,它的价值是无关紧要的。 - Tiago Martins Peres
@Tiago,如果我想在刷新和访问令牌的同时发送一张图片,你有什么好的想法吗? - Lobbel
有没有一种方法可以将IP地址添加到有效载荷中? - FreddCha
@FreddCha 是的,你可以添加任何你想要的东西。 - Tiago Martins Peres

5
在您的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(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'"
    }

1
当我在使用dj-rest-auth和djangorestframework_simplejwt时,寻找在响应中添加令牌过期时间的解决方案时,我在代码(显然未记录?)中找到了一个解决方案:
有一个名为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"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接