[ DRF使用jwt ]


安装

pip install djangorestframework-simplejwt

配置

  • setting.py
INSTALLED_APPS = [
    ...
    'rest_framework_simplejwt',
    ...
]

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


from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': False,
    'UPDATE_LAST_LOGIN': False,

    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    'LEEWAY': 0,

    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

    'JTI_CLAIM': 'jti',

    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
  • url.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    ...
]

案例

  • 认证 通过用户名和密码 获取 ---token
  • post请求-- json格式发送{"username":"xxxx", "password": "xxxx"}
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"
}
  • 通过token即可登录
  • 在请求头headers中添加即可
curl \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNDU2LCJqdGkiOiJmZDJmOWQ1ZTFhN2M0MmU4OTQ5MzVlMzYyYmNhOGJjYSJ9.NHlztMGER7UADHZJlxNG0WSi22a2KaYSfd1S-AuT7lU" \
  http://localhost:8000/api/some-protected-view/
  • 刷新令牌
  • 通过登录时返回的refresh的值
  • post请求 json格式{"refresh": "xxxx"}发送到接口
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"}

重写验证

  • 1.重写生成令牌的token加密内容, 与生成令牌时的返回值
  • 2.重写校验令牌,解密内容返回
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenVerifySerializer
from rest_framework_simplejwt.views import TokenObtainPairView, TokenViewBase
from jwt import decode as jwt_decode
from django.conf import settings

# 生成令牌序列号器
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):

    # 令牌内的加密内容token
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        # Add custom claims
        token['username'] = user.username
        token['email'] = user.email
        print(f'生产令牌的token:"{token}')
        return token

    # 生产令牌返回的数据
    def validate(self, attrs):
        data = super().validate(attrs)

        refresh = self.get_token(self.user)
        data['refresh'] = str(refresh)
        data['access'] = str(refresh.access_token)
        data['username'] = str(refresh.get('username'))
        data['user_id'] = str(refresh.get('user_id'))
        data['email'] = str(refresh.get('email'))
        print(f'生成令牌的校验data:{data}')
        return data

# 生产令牌视图
class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer



# 验证token令牌序列化器
class MyTokenVerifySerializer(TokenVerifySerializer):

    def validate(self, attrs):
        super().validate(attrs)
        decode_data = jwt_decode(attrs['token'], settings.SECRET_KEY, algorithms=["HS256"])
        return decode_data

# 验证序列号器视图
class MyTokenVerifyView(TokenViewBase):
    serializer_class = MyTokenVerifySerializer

setting.py配置详细解析

ACCESS_TOKEN_LIFETIME

  • access令牌有效期

日期时间。timedelta对象,该对象指定访问令牌的有效时间。此时间增量值在令牌生成期间添加到当前UTC时间,以获得令牌的默认“exp”声明值

REFRESH_TOKEN_LIFETIME

  • refresh刷新令牌有效期

日期时间。timedelta对象,该对象指定刷新令牌的有效时间。此时间增量值在令牌生成期间添加到当前UTC时间,以获得令牌的默认“exp”声明值

ROTATE_REFRESH_TOKENS

  • refresh刷新令牌时会更新时间
  • 如果设置了黑名单blacklist_AFTER_ROTION刷新令牌时则加入黑名单

设置为True时,如果向TokenRefreshView提交了刷新令牌,则将返回新的刷新令牌以及新的访问令牌。这个新的刷新令牌将通过JSON响应中的“刷新”键提供。新的刷新令牌将具有更新的到期时间,该到期时间通过将refresh_TOKEN_LIFETIME设置中的时间增量添加到发出请求的当前时间来确定。如果黑名单应用程序正在使用,并且blacklist_AFTER_ROTION设置设置为True,则提交到刷新视图的刷新令牌将添加到黑名单中。

BLACKLIST_AFTER_ROTATION

设置为True时,如果黑名单应用程序正在使用并且ROTATE_FRESH_tokens设置设置为True,则会将提交到TokenRefreshView的刷新令牌添加到黑名单中。您需要添加“rest_framework_simplejwt”。token_blacklist”设置到设置文件中的INSTALLED_APPS以使用此设置。

UPDATE_LAST_LOGIN

当设置为True时,auth_user表中的last_lgin字段将在登录时更新(TokenObtainPairView)。 警告:更新last_login将显著增加数据库事务的数量。人们滥用视图可能会降低服务器速度,这可能是一个安全漏洞。如果你真的想这样做,至少用DRF限制端点

VERIFYING_KEY

用于验证生成令牌内容的验证密钥。如果algorithm设置指定了HMAC算法,则将忽略VERIFYING_KEY设置,并使用SIGNING_KEY设置的值。如果algorithm设置指定了RSA算法,则VERIFYING_KEY设置必须设置为包含RSA公钥的字符串。

AUDIENCE

  • 排除生成令牌/解密令牌中的字段
  • 跳过验证

受众声称被包括在生成的令牌中和/或被解码的令牌中。当设置为“无”时,此字段将从令牌中排除,并且不会被验证。

ISSUER

发行方声称在生成的令牌中包括和/或在解码的令牌中验证。当设置为“无”时,此字段将从令牌中排除,并且不会被验证。

JWK_URL

JWK_URL用于动态解析验证令牌签名所需的公钥。例如,当使用Auth0时,您可以将其设置为“https://yourdomain.auth0.com/.well-known/jwks.json’. 当设置为None时,此字段将从令牌后端排除,并且在验证期间不使用。

AUTH_HEADER_TYPES

  • 配置请求头后传参value的格式
  • “Bearer”

需要身份验证的视图将接受的授权标头类型。例如,值“Bearer”表示需要身份验证的视图将查找以下格式的标头:Authorization:Bearer<token>。此设置还可以包含可能的标头类型的列表或元组(例如(“Bearer”、“JWT”)。如果以这种方式使用列表或元组,并且身份验证失败,则集合中的第一项将用于在响应中构建“WWW-Authenticate”标头。

AUTH_HEADER_NAME

  • 配置请求头传参key的格式
  • HTTP_AUTHORIZATION

用于身份验证的授权标头名称。默认值是HTTP_AUTHORIZATION,它将接受请求中的AUTHORIZATION标头。例如,如果您希望在请求的标头中使用X_Access_Token,请在设置中将AUTH_header_NAME指定为HTTP_X_Access_Token。

USER_ID_FIELD

  • 令牌指定的用户模型id
  • 与下方配合使用 { "user_id":"id"}

用户模型中的数据库字段,将包含在生成的令牌中以标识用户。建议此设置的值指定一个在选择初始值后通常不会更改的字段。例如,指定“用户名”或“电子邮件”字段将是一个糟糕的选择,因为帐户的用户名或电子邮件可能会根据给定服务中帐户管理的设计而改变。这可以允许使用旧用户名创建新帐户,而使用该用户名作为用户标识符的现有令牌仍然有效。

USER_ID_CLAIM

  • 生成的令牌中包含,user_id的key
  • 配合使用 { "user_id":"id"}

生成的令牌中的声明将用于存储用户标识符。例如,设置值“user_id”意味着生成的令牌包含包含用户标识符的“user_id”声明。

USER_AUTHENTICATION_RULE

  • 校验用户身份的规则, 默认规则是user.is_active活跃是否为True

可调用以确定是否允许用户进行身份验证。此规则在处理有效令牌后应用。用户对象作为参数传递给可调用对象。默认规则是检查is_active标志是否仍然为True。可调用函数必须返回布尔值,如果授权,则返回True,否则返回False,从而生成401状态代码。

AUTH_TOKEN_CLASSES

  • 证明用户身份令牌类型, 列表

指向类的点路径列表,这些类指定允许证明身份验证的令牌类型。有关这一点的更多信息,请参阅下面的“令牌类型”部分。

TOKEN_TYPE_CLAIM

用于存储令牌类型的声明名称。有关这一点的更多信息,请参阅下面的“令牌类型”部分。

JTI_CLAIM

  • 标识黑名单中被吊销的令牌

用于存储令牌的唯一标识符的声明名称。此标识符用于标识黑名单应用程序中已吊销的令牌。在某些情况下,可能需要使用默认“jti”声明之外的另一个声明来存储这样的值。

TOKEN_USER_CLASS

一个无状态用户对象,该对象由经过验证的令牌支持。仅用于JWTStatelessUserAuthentication身份验证后端。该值是rest_framework_simplejwt.models.TokenUser子类的虚线路径,也是默认值。

SLIDING_TOKEN_LIFETIME

日期时间。timedelta对象,该对象指定滑动令牌在多长时间内有效以证明身份验证。此时间增量值在令牌生成期间添加到当前UTC时间,以获得令牌的默认“exp”声明值。有关这一点的更多信息,请参阅下面的“滑动令牌”部分。

滑动令牌配置

SLIDING_TOKEN_REFRESH_LIFETIME

日期时间。timedelta对象,该对象指定滑动令牌有效的刷新时间。此时间增量值在令牌生成期间添加到当前UTC时间,以获得令牌的默认“exp”声明值。有关这一点的更多信息,请参阅下面的“滑动令牌”部分。

SLIDING_TOKEN_REFRESH_EXP_CLAIM

用于存储滑动令牌刷新周期的过期时间的声明名称。有关这一点的更多信息,请参阅下面的“滑动令牌”部分。