使用itsdangerous生成确认令牌

来源:互联网 发布:access数据库管理工具 编辑:程序博客网 时间:2024/06/08 11:27

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

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)我们留到下次讲...