django中使用JWT库--djangorestframework-simplejwt

发布时间 2023-12-10 00:57:13作者: 厚礼蝎

安装

在Django项目中安装 djangorestframework-simplejwt

通过以下命令来安装:

pip install djangorestframework-simplejwt

配置

安装完毕后,在Django项目的settings.py文件中进行配置。

1、INSTALLED_APPS

INSTALLED_APPS 中添加 djangorestframework_simplejwt 应用程序:

INSTALLED_APPS = [
    # ...
    'rest_framework_simplejwt',
    # 下面这个app用于刷新refresh_token后,将旧的加到到blacklist时使用
    'rest_framework_simplejwt.token_blacklist'
    # ...
]

2、REST_FRAMEWORK

添加simplejwt到身份验证类列表中:

REST_FRAMEWORK = {
    ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
        ...
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        
    )
    ...
}

3、添加URL路由

以下为根路由中的设置,可以根据需要进行调整

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView
)
​
urlpatterns = [
    ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 下面这个是用来验证token的,根据需要进行配置
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

4、配置SIMPLE_JWT

settings.py文件中添加以下配置:

# JWT配置
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),  # Access Token的有效期
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),  # Refresh Token的有效期
    
    # 对于大部分情况,设置以上两项就可以了,以下为默认配置项目,可根据需要进行调整
    
    # 是否自动刷新Refresh Token
    'ROTATE_REFRESH_TOKENS': False,  
    # 刷新Refresh Token时是否将旧Token加入黑名单,如果设置为False,则旧的刷新令牌仍然可以用于获取新的访问令牌。需要将'rest_framework_simplejwt.token_blacklist'加入到'INSTALLED_APPS'的配置中
    'BLACKLIST_AFTER_ROTATION': False,  
    'ALGORITHM': 'HS256',  # 加密算法
    'SIGNING_KEY': settings.SECRET_KEY,  # 签名密匙,这里使用Django的SECRET_KEY
​
    # 如为True,则在每次使用访问令牌进行身份验证时,更新用户最后登录时间
    "UPDATE_LAST_LOGIN": False, 
    # 用于验证JWT签名的密钥返回的内容。可以是字符串形式的密钥,也可以是一个字典。
    "VERIFYING_KEY": "",
    "AUDIENCE": None,# JWT中的"Audience"声明,用于指定该JWT的预期接收者。
    "ISSUER": None, # JWT中的"Issuer"声明,用于指定该JWT的发行者。
    "JSON_ENCODER": None, # 用于序列化JWT负载的JSON编码器。默认为Django的JSON编码器。
    "JWK_URL": None, # 包含公钥的URL,用于验证JWT签名。
    "LEEWAY": 0, # 允许的时钟偏差量,以秒为单位。用于在验证JWT的过期时间和生效时间时考虑时钟偏差。
​
    # 用于指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer"
    "AUTH_HEADER_TYPES": ("Bearer",), 
    # 包含JWT的HTTP请求头的名称。默认为"HTTP_AUTHORIZATION"
    "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION", 
     # 用户模型中用作用户ID的字段。默认为"id"。
    "USER_ID_FIELD": "id",
     # JWT负载中包含用户ID的声明。默认为"user_id"。
    "USER_ID_CLAIM": "user_id",
    
    # 用于指定用户身份验证规则的函数或方法。默认使用Django的默认身份验证方法进行身份验证。
    "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
​
    #  用于指定可以使用的令牌类。默认为"rest_framework_simplejwt.tokens.AccessToken"。
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
    # JWT负载中包含令牌类型的声明。默认为"token_type"。
    "TOKEN_TYPE_CLAIM": "token_type",
    # 用于指定可以使用的用户模型类。默认为"rest_framework_simplejwt.models.TokenUser"。
    "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
​
    # JWT负载中包含JWT ID的声明。默认为"jti"。
    "JTI_CLAIM": "jti",
​
    # 在使用滑动令牌时,JWT负载中包含刷新令牌过期时间的声明。默认为"refresh_exp"。
    "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
    # 滑动令牌的生命周期。默认为5分钟。
    "SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
    # 滑动令牌可以用于刷新的时间段。默认为1天。
    "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
    # 用于生成访问令牌和刷新令牌的序列化器。
    "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
    # 用于刷新访问令牌的序列化器。默认
    "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
    # 用于验证令牌的序列化器。
    "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
    # 用于列出或撤销已失效JWT的序列化器。
    "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
    # 用于生成滑动令牌的序列化器。
    "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
    # 用于刷新滑动令牌的序列化器。
    "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}

使用

配置完成后,你可以在你的Django REST Framework视图中使用Simple JWT。

以下为官网的示例,也可以使用postman来测试接口。

要验证 Simple JWT 是否正常工作,您可以使用 curl 发出几个测试请求:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"username": "davidattenborough", "password": "boatymcboatface"}' \
  http://localhost:8000/api/token/
...
{ "access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNDU2LCJqdGkiOiJmZDJmOWQ1ZTFhN2M0MmU4OTQ5MzVlMzYyYmNhOGJjYSJ9.NHlztMGER7UADHZJlxNG0WSi22a2KaYSfd1S-AuT7lU", "refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImNvbGRfc3R1ZmYiOiLimIMiLCJleHAiOjIzNDU2NywianRpIjoiZGUxMmY0ZTY3MDY4NDI3ODg5ZjE1YWMyNzcwZGEwNTEifQ.aEoAYkSJjoWH1boshQAaTkf8G3yn0kapko6HFRt7Rh4"
}

您可以使用返回的访问令牌来证明受保护视图的身份验证:

curl \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNDU2LCJqdGkiOiJmZDJmOWQ1ZTFhN2M0MmU4OTQ5MzVlMzYyYmNhOGJjYSJ9.NHlztMGER7UADHZJlxNG0WSi22a2KaYSfd1S-AuT7lU" \
  http://localhost:8000/api/some-protected-view/

当此短期访问令牌过期时,您可以使用长期存在的刷新令牌来获取另一个访问令牌:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImNvbGRfc3R1ZmYiOiLimIMiLCJleHAiOjIzNDU2NywianRpIjoiZGUxMmY0ZTY3MDY4NDI3ODg5ZjE1YWMyNzcwZGEwNTEifQ.aEoAYkSJjoWH1boshQAaTkf8G3yn0kapko6HFRt7Rh4"}' \
  http://localhost:8000/api/token/refresh/
...
{"access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNTY3LCJqdGkiOiJjNzE4ZTVkNjgzZWQ0NTQyYTU0NWJkM2VmMGI0ZGQ0ZSJ9.ekxRxgb9OKmHkfy-zs1Ro_xs1eMLXiR17dIDBVxeT-w"}

自定义token中返回的信息

1、默认的信息

JSON Web Token由三部分组成,这些部分由点(.)分隔,分别是header(头部),payload(有效负载)和signature(签名)。

  • header(头部): 识别以何种算法来生成签名;
  • pyload(有效负载): 用来存放实际需要传递的数据;
  • signature(签名): 安全验证token有效性,防止数据被篡改。

默认的设置中,如果对第二部分进行解码,你就会发现,它保存了一个字典的数据,包括以下信息:

{
  "token_type": "access",  # 令牌类型
  "exp": 1685341009,       # 令牌到期时间戳
  "iat": 1685340709,       # 令牌生成时间戳
  "jti": "561*****************03c",  # JWT ID的声明
  "user_id": 1,            # django中用户对应的id
}

如果需要为它添加更多的用户信息,比如用户名称,用户手机号,email等,只要是系统后台中已存储的,都可以放在第二部分进行返回,由前端进行解码后,就可以提取使用了。具体步骤如下:

2、自定义返回信息

自定义序列化器

该序列化器需继承TokenObtainPairSerializer类,可以在任意一个app中的seralizers.py中增加该自定义的序列化器,并重写了get_token()方法。

在这个方法中,我们可以自定义Payload,将用户的信息添加到Token中。

假定为app01,则在app01/seralizers.py中增加以下代码:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        # 增加想要加到token中的信息
        token['username'] = user.username
        token['email'] = user.email
        # ...
        return token

改写simple JWT提供的默认视图,在app01/views中新增一个视图,该视图需继承至默认的视图类TokenObtainPairView

from .serializers import MyTokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class MyObtainTokenPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer  #只需修改其序列化器为刚刚自定义的即可

然后再将url中的视图改为新增加这个视图类即可。

from rest_framework_simplejwt.views import (
    # TokenObtainPairView, 这一个用不到就可以删除了
    TokenRefreshView,
    TokenVerifyView
)

from app01.views import MyObtainTokenPairView  # 导入自定义的视图类

urlpatterns = [
    ...
    #只需修改这个token接口的视图类就可以了
    path('api/token/', MyObtainTokenPairView.as_view(), name='token_obtain_pair'), 
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 下面这个是用来验证token的,根据需要进行配置
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

大功告成,现在你就拥有了自定义的视图类,通过这个接口访问得到的token就附带了刚刚添加的信息,可以将token中间的那一段进行解码就可以得到如下信息:

{
  "token_type": "access",  # 令牌类型
  "exp": 1685341009,       # 令牌到期时间戳
  "iat": 1685340709,       # 令牌生成时间戳
  "jti": "561*****************03c",  # JWT ID的声明
  "user_id": 1,            # django中用户对应的id
  "username": "user001",            # django中用户对应的用户名
  "email": "*****@163.com",            # django中用户对应的email
    
}