使用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'
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)我們留到下次講...