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

来源:互联网 发布:sn ty gm js是什么 编辑:程序博客网 时间:2024/05/19 03:23

一, 内容回顾

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

    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, 下次再分析吧...看代码看的心累大笑




   


         







原创粉丝点击