django-jwt token校驗源碼簡析


一. jwt token校驗源碼簡析

1.1 前言

  之前使用jwt簽發了token,里面的頭部包含了加密的方式、是否有簽名等,而載荷中包含用戶名、用戶主鍵、過期時間等信息,最后的簽名還使用了摘要算法進行不可逆的加密。

  同時檢驗簽名時還需進行簽名的碰撞檢測,判斷該token是否合法。jwt提供了一個校驗token的認證方法,使用時只需要CBV中進行局部身份驗證配置即可。使用如下:

from rest_framework_jwt.authentication import JSONWebTokenAuthentication


class 類名(JSONWebTokenAuthentication):
    authentication_classes = [JSONWebTokenAuthentication]

  不過自身的校驗還存在一些缺陷,不能完全滿足的需求,這時候我們可以繼承該類后重寫其中校驗token的方法,具體是什么方法,我們接着往下看。

1.2 jwt的authenticate方法

  以前走過APIView的源碼,從源碼得知身份校驗走的是類中的authenticate的方法。定位至JSONWebTokenAuthentication,依據屬性的查找順序找到authenticate方法:

  可以看出主要的方法是以上三個,接下來一一點進去瞅瞅(遵循屬性查找順序),首先是get_jwt_value(request)方法:

  看看get_jwt_value中的get_authorization_header方法:

  回到authenticate方法,此時jwt_value的值要么是None,要么是token:

  再看看重點的校驗token的方法jwt_decode_handler(jwt_value)

  上述jwt.decode返回的載荷,有興趣可以點進去看看源碼,這里直接略過。

  往下看看authenticate_credentials(payload)方法:

  該函數返回一個user對象,最終由authenticate方法返回該user對象及token。不過試驗時發現get_jwt_value方法返回的是None而不是token時,authenticate方法return None,這時也能通過校驗,所以我們可以重寫authenticate方法,對get_jwt_value中用於取頭部token信息的get_authorization_header返回的值進行判斷,增加健壯性。

import jwt
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.authentication import get_authorization_header
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            # 采用drf獲取token的手段 - HTTP_AUTHORIZATION - Authorization
            token = get_authorization_header(request)
            if not token:
                raise AuthenticationFailed('Authorization 字段是必須的')
            # 可以添加反扒措施:原功能是token有前綴

            # drf-jwt認證校驗算法
            try:
                payload = jwt_decode_handler(token)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('簽名過期')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('非法用戶')
            user = self.authenticate_credentials(payload)
            # 將認證結果丟該drf
            return user, token
AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend'

二. jwt的RefreshJSONWebToken

   jwt生成token后,因為token有個過期時間,而這個時間默認是五分鍾,之后客戶端攜帶token重新訪問時,會調用jwt的RefreshJSONWebToken中的RefreshJSONWebTokenSerializer類中的validate方法,來刷新客戶端的token,而刷新的時間也是有上限的,默認是七天,可以在項目下的配置中自定義。

# 配置的默認過期時間
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300)

# token刷新的最大時間間隔,默認七天
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7)

   如自定義過期時間:

import datetime
JWT_AUTH = {
    # 過期時間
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}

  接下來看看RefreshJSONWebTokenSerializer的源碼。

  讓我們看看validate方法:

  validate方法源碼如下:

def validate(self, attrs):
    token = attrs['token']

    payload = self._check_payload(token=token)
    user = self._check_user(payload=payload)
    # Get and check 'orig_iat'
    orig_iat = payload.get('orig_iat')

    if orig_iat:
        # Verify expiration
        refresh_limit = api_settings.JWT_REFRESH_EXPIRATION_DELTA

        if isinstance(refresh_limit, timedelta):
            refresh_limit = (refresh_limit.days * 24 * 3600 +
                             refresh_limit.seconds)

        expiration_timestamp = orig_iat + int(refresh_limit)
        now_timestamp = timegm(datetime.utcnow().utctimetuple())

        if now_timestamp > expiration_timestamp:
            msg = _('Refresh has expired.')
            raise serializers.ValidationError(msg)
    else:
        msg = _('orig_iat field is required.')
        raise serializers.ValidationError(msg)

    new_payload = jwt_payload_handler(user)
    new_payload['orig_iat'] = orig_iat

    return {
        'token': jwt_encode_handler(new_payload),
        'user': user
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM