rest framework simplejwt源码解析

一、rest framework simplejwt的安装

pip install djangorestframework-simplejwt

然后在settings.py中添加:

INSTALLED_APPS = [
    ...
    'rest_framework_simplejwt',
    ...
]

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

在settings.py中添加:

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),
}

 添加获取jwt和刷新jwt的路由

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

 二、rest framework simplejwt源码

simplejwt引入的两个视图,分别是TokenObtainPairView和TokenRefreshView。

class TokenObtainPairView(TokenViewBase):

    _serializer_class = api_settings.TOKEN_OBTAIN_SERIALIZER

token_obtain_pair = TokenObtainPairView.as_view()

TokenObtainPairView和TokenRefreshView的父类都是TokenViewBase,TokenObtainPairView的as_view方法和restframework中介绍的as_view一样,在反射后会调用post方法,因为TokenObtainPairSerializer本身没有post方法,所以会调用基类TokenObtainSerializer的post方法。

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        try:
            serializer.is_valid(raise_exception=True)
        except TokenError as e:
            raise InvalidToken(e.args[0])

        return Response(serializer.validated_data, status=status.HTTP_200_OK)

在get_serializer方法中,如果TokenObtainPairView类中_serializer_class等于None,那么get_serializer方法就会将TokenObtainPairSerializer类的对象赋给serializer。

如果需要自定义serializer,那么就只需要在自定义视图类中将自定义的TokenObtainPairSerializer类赋给_serializer_class

在基类的post方法中,serializer是TokenObtainPairSerializer类的对象。在最后会调用TokenObtainPairSerializer的validated_data方法。

class TokenObtainPairSerializer(TokenObtainSerializer):

    token_class = RefreshToken

    def validate(self, attrs):

        data = super().validate(attrs)

        refresh = self.get_token(self.user)

        data["refresh"] = str(refresh)
        data["access"] = str(refresh.access_token)

        if api_settings.UPDATE_LAST_LOGIN:
            update_last_login(None, self.user)

        return data

validate方法又调用了父类的validate方法,下面是父类的validate方法

    def validate(self, attrs):
        authenticate_kwargs = {
            self.username_field: attrs[self.username_field],
            "password": attrs["password"],
        }
        try:
            authenticate_kwargs["request"] = self.context["request"]
        except KeyError:
            pass

        self.user = authenticate(**authenticate_kwargs)

        if not api_settings.USER_AUTHENTICATION_RULE(self.user):
            raise exceptions.AuthenticationFailed(
                self.error_messages["no_active_account"],
                "no_active_account",
            )

        return {}

其中的authenticate方法会返回用户名和密码都正确的user对象并赋给self.user。

validate方法是用于验证token的,一共有两个参数,第一个参数是TokenObtainPairSerializer对象

    @classmethod
    def get_token(cls, user):
        return cls.token_class.for_user(user)

其中cls.token_class是rest_framework_simplejwt.tokens.RefreshToken类,因为RefreshToken类没有for_user方法,所以调用了基类Token的for_user方法

    @classmethod
    def for_user(cls, user):
        """
        Returns an authorization token for the given user that will be provided
        after authenticating the user's credentials.
        """
        user_id = getattr(user, api_settings.USER_ID_FIELD)

        if not isinstance(user_id, int):
            user_id = str(user_id)

        token = cls()
        token[api_settings.USER_ID_CLAIM] = user_id

        return token

token=cls(),调用rest_framework_simplejwt.tokens.RefreshToken的构造函数,同样因为RefreshToken本身没有__init__方法,所以调用了基类Token的__init__方法。

最后在token[api_settings.USER_ID_CLAIM] = user_id中将user_id放入token中。

    def __init__(self, token=None, verify=True):
        if self.token_type is None or self.lifetime is None:
            raise TokenError(_("Cannot create token with no type or lifetime"))

        self.token = token
        self.current_time = aware_utcnow()
        # Set up token
        if token is not None:
            # An encoded token was provided
            token_backend = self.get_token_backend()

            # Decode token
            try:
                self.payload = token_backend.decode(token, verify=verify)
            except TokenBackendError:
                raise TokenError(_("Token is invalid or expired"))

            if verify:
                self.verify()
        else:
            # 新的Token. 跳过所有验证步骤.
            self.payload = {api_settings.TOKEN_TYPE_CLAIM: self.token_type}
            # Set "exp" and "iat" claims with default value
            self.set_exp(from_time=self.current_time, lifetime=self.lifetime)
            self.set_iat(at_time=self.current_time)

            # Set "jti" claim
            self.set_jti()

在初始化token的过程中,首先初始化token的payload,分别设置payload的token_type、exp、iat和jti。

三、自定义视图

首先需要在api应用中新建文件夹utils,然后新建auth.py,在auth.py中新建视图类MyTokenObtainPairView和MyTokenObtainPairSerializer类

class MyTokenObtainPairView(TokenObtainPairView):
    _serializer_class = MyTokenObtainPairSerializer

在类中只有一行,就是设置_serializer_class为新建的MyTokenObtainPairSerializer类。

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    username_field = 'userID'

    def validate(self, attrs):
        # data = super().validate(attrs)
        password = make_password(attrs["password"])
        authenticate_kwargs = {
            self.username_field: attrs[self.username_field],
            "password": attrs["password"],
        }
        try:
            user = Teachers.objects.get(**authenticate_kwargs)
        except Exception as e:
            raise exceptions.NotFound(e.args[0])

        refresh = self.get_token(user)
        data = {"access": str(refresh.access_token), "refresh": str(refresh)}

        return data

在MyTokenObtainPairSerializer类中重写了validate方法,该方法会取出用户请求中的username_field和password值,然后获取符合该条件的对象,再利用get_token函数生成新的token

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值