1. 程式人生 > >使用itsdangerous生成確認令牌

使用itsdangerous生成確認令牌

首先我們來看生成令牌和解碼令牌的程式碼:

from itsdangerous import TimedJSONWebSignatureSerializer

    s = TimedJSONWebSignatureSerializer(app.config['SECRET_KEY'], expires_in=3600)

    token = s.dumps({'confirm': 23})

    data = s.loads(token)

根據我的理解,Serializer構造了它的例項s,它的第一個引數是我們設定的config變數,也就是金鑰。

然後例項呼叫dumps函式, 用金鑰對資料進行了加密, 生成令牌字串token。

最後s呼叫loads函式,用金鑰對令牌進行解碼, 得到data字典。

那麼這個過程中到底發生了什麼?

******************************************************************************************************

一:我們首先來看TimedJSONWebSignatureSerializer類的建構函式:

*********************************************************************************************************

class TimedJSONWebSignatureSerializer(JSONWebSignatureSerializer):
    DEFAULT_EXPIRES_IN = 3600

    def __init__(self, secret_key, expires_in=None, **kwargs):
        JSONWebSignatureSerializer.__init__(self, secret_key, **kwargs)
        if expires_in is None:
            expires_in = self.DEFAULT_EXPIRES_IN
        self.expires_in = expires_in

    我們可以看到TimedJSONWebSignatureSerializer繼承了JSONWebSignatureSerializer類。

        (1)在建構函式中呼叫了父類的建構函式。我們傳入的引數是:

            secret_key=app.config['SECRET_KEY']

        (2)然後設定了例項的expires_in屬性 = 3600.

二:下面我們來看JSONWebSignatureSerializer的建構函式:

class JSONWebSignatureSerializer(Serializer):
    jws_algorithms = {
        'HS256': HMACAlgorithm(hashlib.sha256),
        'HS384': HMACAlgorithm(hashlib.sha384),
        'HS512': HMACAlgorithm(hashlib.sha512),
        'none': NoneAlgorithm(),
    }
    default_algorithm = 'HS256'

    default_serializer = compact_json
    def __init__(self, secret_key, salt=None, serializer=None,
                 signer=None, signer_kwargs=None, algorithm_name=None):
        Serializer.__init__(self, secret_key, salt, serializer,
                            signer, signer_kwargs)
        if algorithm_name is None:
            algorithm_name = self.default_algorithm
        self.algorithm_name = algorithm_name
        self.algorithm = self.make_algorithm(algorithm_name)

    JSONWebSignatureSerializer繼承了Serializer類

        (1)在建構函式中呼叫了父類的建構函式,引數除了secret_key,其他的都預設是None。

        (2)因為algorithm_name=None, 所以設定了例項屬性

            self.algrithm_name = 'HS256'

        (3)然後設定了屬性

            self.algorithm = self.make_algorithm(algorithm_name) 引數就是‘HS256’

           1.make_algorithm函式:

    def make_algorithm(self, algorithm_name):
        try:
            return self.jws_algorithms[algorithm_name]
        except KeyError:
            raise NotImplementedError('Algorithm not supported')

          2.self.jws_algorithms是JSONWebSignatureSerializer的類變數,是一個字典(上文可見):

              查字典我們可知,make_algorithm函式返回的是HMACAlgorithm(hashlib.sha256)

          所以例項屬性self.algorithm = HMACAlgorithm(hashlib.sha256)

總結一下:JSONWebSignatureSerializer的建構函式做了兩件事

    1.呼叫父類Serializer的建構函式Serializer.__init__(self, secret_key, salt, serializer, signer, signer_kwargs)

    2.設定例項屬性self.algorithm_name = ‘HS256’; 設定例項屬性self.algorithm = HMACAlgorithm(hashlib.sha256)

三:下面我們來看Serializer的建構函式:

class Serializer(object):
    default_serializer = json
    default_signer = Signer
    def __init__(self, secret_key, salt=b'itsdangerous', serializer=None,
                 signer=None, signer_kwargs=None):
        self.secret_key = want_bytes(secret_key)
        self.salt = want_bytes(salt)
        if serializer is None:
            serializer = self.default_serializer
        self.serializer = serializer
        self.is_text_serializer = is_text_serializer(serializer)
        if signer is None:
            signer = self.default_signer
        self.signer = signer
        self.signer_kwargs = signer_kwargs or {}
    (1)首先設定例項屬性self.secret_key = wantbytes(secret_key)

         (* wantbytes函式的作用是判斷引數字串是不是unicode型別, 如果是把它編碼成utf-8,, 就是把u''字串編碼成b'')

    (2)然後設定例項屬性self.salt = wantbytes(b'itsdangerous')

    (3)然後設定例項屬性self.serializer = json

    (4)然後設定例項屬性self.is_text_serializer = is_text_serializer(serializer)

        1. is_text_serializer函式:

def is_text_serializer(serializer):
    """Checks wheather a serializer generates text or binary."""
    return isinstance(serializer.dumps({}), text_type)

         text_type = unicode

         可以知道這個函式是檢查serializer.dumps({})生成的是不是unicode字串;如果是:self.is_text_serializer = true;

         self.is_text_serializer = flase

    (5)然後設定屬性self.signer = Signer

    (6)然後設定屬性self.signer_kwargs = {}

總結一下, 當我們呼叫 s = Serializer(app.config['SECRET_KEY'], expires_in=3600)的時候建立了例項s 併為這個例項設定了一系列的屬性, 請留意這些例項屬性,下文我們會用到。

*****************************************************************************************************************************************************************

 二:然後我們來看 token = s.dumps({'confirm': 23})

******************************************************************************************************************************************************************

dumps函式:

    def dumps(self, obj, salt=None, header_fields=None):
        """Like :meth:`~Serializer.dumps` but creates a JSON Web Signature.  It
        also allows for specifying additional fields to be included in the JWS
        Header.
        """
        header = self.make_header(header_fields)
        signer = self.make_signer(salt, self.algorithm)
        return signer.sign(self.dump_payload(header, obj))

(1)header = self.make_header(header_fields)

 make_header函式:

    def make_header(self, header_fields):
        header = JSONWebSignatureSerializer.make_header(self, header_fields)
        iat = self.now()
        exp = iat + self.expires_in
        header['iat'] = iat
        header['exp'] = exp
        return header

    1. header = JSONWebSignatureSerializer.make_header(self, header_fields)

    def make_header(self, header_fields):
        header = header_fields.copy() if header_fields else {}
        header['alg'] = self.algorithm_name
        return header
        結果是header = {'alg': ‘HS256’}

    2.iat = self.now()

        now()函式返回當前的時間戳

    3.exp=當前時間加上期待時間, exp就是過期的時間

總結一下, 最後make_header函式返回的是字典{‘alg’: 'HS256', 'iat': 當前時間, 'exp': 過期時間}

header = self.make_header(header_fields) = {‘alg’: 'HS256', 'iat': 當前時間, 'exp': 過期時間}

(2)signer = self.make_signer(salt, self.algorithm) = self.make_signer(None, HMACAlgorithm(hashlib.sha256)))

     make_signer函式:

    def make_signer(self, salt=None, algorithm=None):
        if salt is None:
            salt = self.salt
        key_derivation = 'none' if salt is None else None
        if algorithm is None:
            algorithm = self.algorithm
        return self.signer(self.secret_key, salt=salt, sep='.',
            key_derivation=key_derivation, algorithm=algorithm)

    例項屬性self.salt = wantbytes(b'itsdangerous')

    所以函式最後返回的是,

          return self.singer(app.config['SECRET_KEY'], salt=b'itsdangerous',  sep='.', key_derivation=None, algorithm=HMACAlgorithm(hashlib.sha256))

    例項屬性self.signer = Signer (類), 所以函式最後返回的是

          return Singer(app.config['SECRET_KEY'], salt=b'itsdangerous',  sep='.', key_derivation=None, algorithm=HMACAlgorithm(hashlib.sha256))

Singer是一個類, 其建構函式如下:

class Signer(object):
    default_digest_method = staticmethod(hashlib.sha1)
    default_key_derivation = 'django-concat'
    def __init__(self, secret_key, salt=None, sep='.', key_derivation=None,
                 digest_method=None, algorithm=None):
        self.secret_key = want_bytes(secret_key)
        self.sep = sep
        self.salt = 'itsdangerous.Signer' if salt is None else salt
        if key_derivation is None:
            key_derivation = self.default_key_derivation
        self.key_derivation = key_derivation
        if digest_method is None:
            digest_method = self.default_digest_method
        self.digest_method = digest_method
        if algorithm is None:
            algorithm = HMACAlgorithm(self.digest_method)
        self.algorithm = algorithm

總結一下,signer = self.make_signer(salt, self.algorithm), singer是Singer類的一個例項,singer的屬性如下

singer.secret_key = app.config['SECRET_KEY']

singer.sep = '.'

singer.salt = b'itsdangerous'

singer.key_derivation = 'django-concat'

singer.digest_method = staticmethod(hashlib.sha1)

singer.algorithm = HMACAlgorithm(hashlib.sha256)

(3) return signer.sign(self.dump_payload(header, obj))

上文提到,header = {‘alg’: 'HS256', 'iat': 當前時間, 'exp': 過期時間}

obj = {'confirm': 23}

1.dump_payload函式

    def dump_payload(self, header, obj):
        base64d_header = base64_encode(self.serializer.dumps(header))
        base64d_payload = base64_encode(self.serializer.dumps(obj))
        return base64d_header + b'.' + base64d_payload
    1.1self.serializer.dumps(header)

        例項屬性self.serializer = json

        所以這就就等價於json.dumps({‘alg’: 'HS256', 'iat': 當前時間, 'exp': 過期時間})

        json.dumps把字典轉化成str型別, 然後用base64_encode對這個str進行編碼。

    所以dump_payload函式返回的就是header字典的字串base64編碼 + obj字典的字串的base64編碼 中間用 ‘.’進行分隔

2.signer.sign函式:

    def sign(self, value):
        """Signs the given string."""
        return value + want_bytes(self.sep) + self.get_signature(value)

value就是上面dump_payload的返回值

    2.1 get_signature函式:

    def get_signature(self, value):
        """Returns the signature for the given value"""
        value = want_bytes(value)
        key = self.derive_key()
        sig = self.algorithm.get_signature(key, value)
        return base64_encode(sig)

        2.1.1   key = self.derive_key()

    def derive_key(self):
        """This method is called to derive the key.  If you're unhappy with
        the default key derivation choices you can override them here.
        Keep in mind that the key derivation in itsdangerous is not intended
        to be used as a security method to make a complex key out of a short
        password.  Instead you should use large random secret keys.
        """
        salt = want_bytes(self.salt)
        if self.key_derivation == 'concat':
            return self.digest_method(salt + self.secret_key).digest()
        elif self.key_derivation == 'django-concat':
            return self.digest_method(salt + b'signer' +
                self.secret_key).digest()
        elif self.key_derivation == 'hmac':
            mac = hmac.new(self.secret_key, digestmod=self.digest_method)
            mac.update(salt)
            return mac.digest()
        elif self.key_derivation == 'none':
            return self.secret_key
        else:
            raise TypeError('Unknown key derivation method')
 
           前文我們提到self.key_derivation == 'django-concat', 所以執行  return self.digest_method(salt + b'signer' +self.secret_key).digest()

           上文提到self.digest_method = staticmethod(hashlib.sha1)

           所以上上句就等價於staticmethod(hashlib.sha1)(salt + b'signer' +self.secret_key).digest()

           等價於 hashlib.sha1(b'itsdangerous' + b'signer' + app.config['SECRECT_KEY']).digest()

           這就程式碼意思就是對引數字串進行sha1加密, 返回加密後的字串。

     key = b'itsdangerous' + b'signer' + app.config['SECRECT_KEY']  sha1加密後的字串。

    2.1.2 sig = self.algrothm.get_signature(key, value)  

        例項屬性self.algrothm = HMACAlgorithm(hashlib.sha256) 所以self.algrothm.get_signature(key, value) 等價於

          HMACAlgorithm(hashlib.sha256).get_signature(key, value)

         1. HMACAlgorithm的建構函式:

class HMACAlgorithm(SigningAlgorithm):
    default_digest_method = staticmethod(hashlib.sha1)
    def __init__(self, digest_method=None):
        if digest_method is None:
            digest_method = self.default_digest_method
        self.digest_method = digest_method

         HMACAlgorithm(hashlib.sha256)建立了HMACAlgorithm類的一個例項, 並設定例項屬性digest_method = hashlib.sha256

        2.緊接著這個例項呼叫get_signature方法:

    def get_signature(self, key, value):
        mac = hmac.new(key, msg=value, digestmod=self.digest_method)
        return mac.digest()

            hmac鍵值對加密, key作為鍵, value 作為值, 用hashlib.sha256進行加密,所以

       sig = self.algrothm.get_signature(key, value) = key作為鍵, value 作為值, 用hashlib.sha256進行加密後的字串

所以2.1 get_signature函式返回的是對sig base64編碼後的字串

所以2signer.sign函式返回的是

return value + want_bytes(self.sep) + self.get_signature(value)

所以dumps函式返回的就是, header字典的字串base64編碼 + obj字典的字串的base64編碼 中間用 ‘.’進行分隔 (value)+  ‘.’  + key作為鍵, value 作為值, 用hashlib.sha256進行加密後的字串的base64編碼    也就是token!

data = s.loads(token)我們留到下次講...