python flaskweb开发 data = s.loads(token)原理分析

一, 内容回顾

    首先我们来看生成令牌和解码令牌的代码:

    from itsdangerous import TimedJSONWebSignatureSerializer

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

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

        data = s.loads(token)


    上次我们讲到dumps函数生成的token是 (1) 和 (2)用 ‘.’连接起来的字符串

        (1)header字典({‘alg’: 'HS256', 'iat': 当前时间, 'exp': 过期时间})的字符串base64编码 + obj字典({'confirm': 23})的字符串的base64编码 中间用 ‘.’进行分隔 (value)

        (2)key(b'itsdangerous' + b'signer' + app.config['SECRECT_KEY']  sha1加密后的字符串)作为键, value(就是(1)) 作为值, 用hashlib.sha256进行加密后的字符串的base64编码


小知识:
python2.x里, b前缀没什么具体意义, 只是为了兼容python3.x的这种写法

python3.x里默认的str是(py2.x里的)unicode, bytes是(py2.x)的str, b”“前缀代表的就是bytes

这里我们用的是python2.x, 所以不用去考虑b

二,这节我们来看data = s.loads(token), 分析loads函数是如何把token还原为data的:

我们首先来看loads函数:

参数s被赋值为token, salt和return_header采用默认值None和False。

    def loads(self, s, salt=None, return_header=False):
        payload, header = JSONWebSignatureSerializer.loads(
            self, s, salt, return_header=True)

        if 'exp' not in header:
            raise BadSignature('Missing expiry date', payload=payload)

        if not (isinstance(header['exp'], number_types)
                and header['exp'] > 0):
            raise BadSignature('expiry date is not an IntDate',
                               payload=payload)

        if header['exp'] < self.now():
            raise SignatureExpired('Signature expired', payload=payload,
                                   date_signed=self.get_issue_date(header))

        if return_header:
            return payload, header
        return payload

(1)TimedJSONWebSignatureSerializer的loads方法中调用了父类的loads方法:

    s=token , salt=None, return_header=True

    def loads(self, s, salt=None, return_header=False):
        """Reverse of :meth:`dumps`. If requested via `return_header` it will
        return a tuple of payload and header.
        """
        payload, header = 
        if header.get('alg') != self.algorithm_name:
            raise BadHeader('Algorithm mismatch', header=header,
                            payload=payload)
        if return_header:
            return payload, header
        return payload

    1.        payload, header = self.load_payload(self.make_signer(salt, self.algorithm).unsign(want_bytes(s)), return_header=True)

        1.1 self.make_signer(salt, self.algorithm)

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

    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')= ‘itsdangrous’  wantbytes函数的作用是把unicode字符串转换成str。

           key_derivation = None

          所以make_signer函数最后返回的是self.signer(app.config['SECRECT_KEY'], salt='itsdangerous', sep = '.',key_derivation=None,algorithm=HMACAlgorithm(hashlib.sha256)')

          self.signer = Signer, 即Singer(app.config['SECRECT_KEY'], salt='itsdangerous', sep = '.', key_derivation=None, algorithm=HMACAlgorithm(hashlib.sha256))

          Singner的构造函数:

         

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
          所以Singer(app.config['SECRECT_KEY'], salt=b'itsdangerous', sep = '.', key_derivation=None, algorithm='HS256')创建了一个Singner类的实例,并设置了如下属性:

          self.secrect_key = want_bytes(app.config['SECRECT_KEY'])

          self.sep = '.'

          self.salt = b'itsdangrous'

          self.key_derivation = 'django-concat'

          self.difest_method = staticmethod(hashlib.sha1)

          self.algorithm = 'HS256'


          结论:所以self.make_signer(salt, self.algorithm)返回的是Singner的实例


         然后这个实例调用unsign方法: self.make_signer(salt, self.algorithm).unsign(want_bytes(s))

          s=token

    def unsign(self, signed_value):
        """Unsigns the given string."""
        signed_value = want_bytes(signed_value)
        sep = want_bytes(self.sep)
        if sep not in signed_value:
            raise BadSignature('No %r found in value' % self.sep)
        value, sig = signed_value.rsplit(sep, 1)
        if self.verify_signature(value, sig):
            return value
        raise BadSignature('Signature %r does not match' % sig,
                           payload=value)
          这个函数把token分成了(1) (2)两部分, 然后调用了verify_signatue函数:

    def verify_signature(self, value, sig):
        """Verifies the signature for the given value."""
        key = self.derive_key()
        try:
            sig = base64_decode(sig)
        except Exception:
            return False
        return self.algorithm.verify_signature(key, value, sig)
         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')

           这个函数返回 staticmethod(hashlib.sha1)(b'itsdangrous' + b'signer' + app.config['SECRECT_KEY']).digest(),赋值给key

           然后把(2)进行base64解码给sig

           value 是(1)

           verify_signatue函数返回self.algorithm.verify_signature(key, value, sig) 即HMACAlgorithm(hashlib.sha256).verify_signature(key, value, sig)

           创建了HMACAlgorithm类的实例, 并设置实例属性self.digest_method = hashlib.sha256,然后调用父类verify_signature方法,

    def verify_signature(self, key, value, sig):
        """Verifies the given signature matches the expected signature"""
        return constant_time_compare(sig, self.get_signature(key, value))
            子类self.get_signature方法,返回的就是key作为键, value作为值, 用hashlib.sha256加密后的字符串

            和sig一样, 所以constant_time_compare函数返回True,

            结论:所以unsign函数返回value。

      self.load_payload(value, return_header=True), load_payload函数:

      

    def load_payload(self, payload, return_header=False):
        payload = want_bytes(payload)
        if b'.' not in payload:
            raise BadPayload('No "." found in value')
        base64d_header, base64d_payload = payload.split(b'.', 1)
        try:
            json_header = base64_decode(base64d_header)
        except Exception as e:
            raise BadHeader('Could not base64 decode the header because of '
                'an exception', original_error=e)
        try:
            json_payload = base64_decode(base64d_payload)
        except Exception as e:
            raise BadPayload('Could not base64 decode the payload because of '
                'an exception', original_error=e)
        try:
            header = Serializer.load_payload(self, json_header,
                serializer=json)
        except BadData as e:
            raise BadHeader('Could not unserialize header because it was '
                'malformed', original_error=e)
        if not isinstance(header, dict):
            raise BadHeader('Header payload is not a JSON object',
                header=header)
        payload = Serializer.load_payload(self, json_payload)
        if return_header:
            return payload, header
        return payload
           我们知道, value就是 header字典({‘alg’: 'HS256', 'iat': 当前时间, 'exp': 过期时间})的字符串base64编码 + obj字典({'confirm': 23})的字符串的base64编码 中间用 ‘.’进行分隔 (value)

          这个函数把两个编码后的字符串分割开,然后分别解码成字典字符串,然后分别调用Serializer.load_payload函数:

    def load_payload(self, payload, serializer=None):
        """Loads the encoded object.  This function raises :class:`BadPayload`
        if the payload is not valid.  The `serializer` parameter can be used to
        override the serializer stored on the class.  The encoded payload is
        always byte based.
        """
        if serializer is None:
            serializer = self.serializer
            is_text = self.is_text_serializer
        else:
            is_text = is_text_serializer(serializer)
        try:
            if is_text:
                payload = payload.decode('utf-8')
            return serializer.loads(payload)
        except Exception as e:
            raise BadPayload('Could not load the payload because an '
                'exception occurred on unserializing the data',
                original_error=e)



先看header = Serializer.load_payload(self, json_header, serializer=json)

json_header就是"{‘alg’: 'HS256', 'iat': 当前时间, 'exp': 过期时间}"

所以函数返回的就是字典字符串 json.loads后得到的字典 , header = {‘alg’: 'HS256', 'iat': 当前时间, 'exp': 过期时间}


再看payload = Serializer.load_payload(self, json_payload)

json_payload就是"{'confirm': 23}"

所以函数返回的就是字典字符串json.loads后的到的字典, payload ={'confirm': 23}


结论:header = {‘alg’: 'HS256', 'iat': 当前时间, 'exp': 过期时间}    payload ={'confirm': 23}


然后我们回到JSONWebSignatureSerializer的loads函数:

    def loads(self, s, salt=None, return_header=False):
        """Reverse of :meth:`dumps`. If requested via `return_header` it will
        return a tuple of payload and header.
        """
        payload, header = self.load_payload(
            self.make_signer(salt, self.algorithm).unsign(want_bytes(s)),
            return_header=True)
        if header.get('alg') != self.algorithm_name:
            raise BadHeader('Algorithm mismatch', header=header,
                            payload=payload)
        if return_header:
            return payload, header
        return payload

现在header和payload都有了, 返回payload和header到子类TimedJSONWebSignatureSerializer的loads函数:

    def loads(self, s, salt=None, return_header=False):
        payload, header = JSONWebSignatureSerializer.loads(
            self, s, salt, return_header=True)

        if 'exp' not in header:
            raise BadSignature('Missing expiry date', payload=payload)

        if not (isinstance(header['exp'], number_types)
                and header['exp'] > 0):
            raise BadSignature('expiry date is not an IntDate',
                               payload=payload)

        if header['exp'] < self.now():
            raise SignatureExpired('Signature expired', payload=payload,
                                   date_signed=self.get_issue_date(header))

        if return_header:
            return payload, header
        return payload

返回payload,{'confirm': 23}, 所以data = s.loads(token), data的值就是{'confirm': 23}!!!!!!!

现在问题来了, 怎么从token到data绕了这么大一圈???


我猜主要是为了防止有人伪造token, 所以重点还是在app.config['SECRECT_KEY']和id, 下次再分析吧...看代码看的心累大笑




   


         







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值