• python的模块itsdangerous


    这个模块主要用来签名和序列化

    使用场景:

    一、给字符串添加签名:

      发送方和接收方拥有相同的密钥--"secret-key",发送方使用密钥对发送内容进行签名,接收方使用相同的密钥对接收到的内容进行验证,看是否是发送方发送的内容

     1 >>> from itsdangerous import Signer
     2 >>> s = Signer('secret-key')
     3 >>> s.sign('my string, ssssssssss,dddddddddddddlsd')
     4 'my string, ssssssssss,dddddddddddddlsd.nSXTxgO_UMN4gkLZcFCioa-dZSo'
     5 >>>
     6 >>> s.unsign('my string, ssssssssss,dddddddddddddlsd.nSXTxgO_UMN4gkLZcFCioa-dZSo')
     7 'my string, ssssssssss,dddddddddddddlsd'
     8 >>> s.unsign('my string, ssss.nSXTxgO_UMN4gkLZcFCioa-dZSo')
     9 Traceback (most recent call last):
    10   File "<stdin>", line 1, in <module>
    11   File "/usr/local/lib/python2.7/site-packages/itsdangerous.py", line 374, in unsign
    12     payload=value)
    13 itsdangerous.BadSignature: Signature 'nSXTxgO_UMN4gkLZcFCioa-dZSo' does not match
    14 >>> s.unsign('my string, ssssssssss,dddddddddddddlsd.nSXTxgO_UMN4gkLZcFCioa-dZSP')
    15 Traceback (most recent call last):
    16   File "<stdin>", line 1, in <module>
    17   File "/usr/local/lib/python2.7/site-packages/itsdangerous.py", line 374, in unsign
    18     payload=value)
    19 itsdangerous.BadSignature: Signature 'nSXTxgO_UMN4gkLZcFCioa-dZSP' does not match
    20 >>>

    二、带时间戳的签名:

      签名有一定的时效性,发送方发送时,带上时间信息,接收方判断多长时间内是否失效

    >>> from itsdangerous import TimestampSigner
    >>> s = TimestampSigner('secret-key')
    >>> string = s.sign('foo')
    >>> s.unsign(string, max_age=5)
    foo
    >>> s.unsign(string, max_age=5) Traceback (most recent call last): ... itsdangerous.SignatureExpired: Signature age 15 > 5 seconds

    三、序列化

    >>> from itsdangerous import Serializer
    >>> s = Serializer('secret-key')
    >>> s.dumps([1, 2, 3, 4])
    '[1, 2, 3, 4].r7R9RhGgDPvvWl3iNzLuIIfELmo'
    And it can of course also load:
    
    >>> s.loads('[1, 2, 3, 4].r7R9RhGgDPvvWl3iNzLuIIfELmo')
    [1, 2, 3, 4]
    If you want to have the timestamp attached you can use the TimedSerializer.

    四、带时间戳的序列化:

    >>> from itsdangerous import TimedSerializer
    >>> s=TimedSerializer('secret-key')
    >>> s.dumps([1,2,3,4])
    '[1, 2, 3, 4].DI7WHQ.yVOjwQWau5mVRGuVkoqa7654VXc'
    >>> s.loads('[1, 2, 3, 4].DI7WHQ.yVOjwQWau5mVRGuVkoqa7654VXc')
    [1, 2, 3, 4]
    >>> s.loads('[1, 2, 3, 4].DI7WHQ.yVOjwQWau5mVRGuVkoqa7654VXc',max_age=10)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python2.7/site-packages/itsdangerous.py", line 643, in loads
        .unsign(s, max_age, return_timestamp=True)
      File "/usr/local/lib/python2.7/site-packages/itsdangerous.py", line 463, in unsign
        date_signed=self.timestamp_to_datetime(timestamp))
    itsdangerous.SignatureExpired: Signature age 28 > 10 seconds
    >>> s.loads('[1, 2, 3, 4].DI7WHQ.yVOjwQWau5mVRGuVkoqa7654VXc',max_age=40)
    [1, 2, 3, 4]
    >>>

    五、URL安全序列化

    对于限定字符串的场景,你可以使用URL安全序列化

    >>> from itsdangerous import URLSafeSerializer
    >>> s = URLSafeSerializer('secret-key')
    >>> s.dumps([1, 2, 3, 4])
    'WzEsMiwzLDRd.wSPHqC0gR7VUqivlSukJ0IeTDgo'
    >>> s.loads('WzEsMiwzLDRd.wSPHqC0gR7VUqivlSukJ0IeTDgo')
    [1, 2, 3, 4]

    六、JSON Web签名

    JSON Web Signatures

    Starting with “itsdangerous” 0.18 JSON Web Signatures are also supported. They generally work very similar to the already existing URL safe serializer but will emit headers according to the current draft (10) of the JSON Web Signature (JWS) [draft-ietf-jose-json-web-signature].

    >>> from itsdangerous import JSONWebSignatureSerializer
    >>> s = JSONWebSignatureSerializer('secret-key')
    >>> s.dumps({'x': 42})
    'eyJhbGciOiJIUzI1NiJ9.eyJ4Ijo0Mn0.ZdTn1YyGz9Yx5B5wNpWRL221G1WpVE5fPCPKNuc6UAo'

    When loading the value back the header will not be returned by default like with the other serializers. However it is possible to also ask for the header by passing return_header=True. Custom header fields can be provided upon serialization:

    >>> s.dumps(0, header_fields={'v': 1})
    'eyJhbGciOiJIUzI1NiIsInYiOjF9.MA.wT-RZI9YU06R919VBdAfTLn82_iIQD70J_j-3F4z_aM'
    >>> s.loads('eyJhbGciOiJIUzI1NiIsInYiOjF9.MA.wT-RZI9YU06R919VBdAf'
    ...         'TLn82_iIQD70J_j-3F4z_aM', return_header=True)
    ...
    (0, {u'alg': u'HS256', u'v': 1})
    

    “itsdangerous” only provides HMAC SHA derivatives and the none algorithm at the moment and does not support the ECC based ones. The algorithm in the header is checked against the one of the serializer and on a mismatch a BadSignatureexception is raised.

    七、带时间戳的JSON Web签名

    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer 
    s = Serializer('secret-key', expires_in=60)
    s.dumps({'id': user.id}) # user为model中封装过的对象

     

    八、盐值

    这里的盐值和加密算法里的盐值概念不一样,这里的盐值(salt)可以应用到上面所有情形中,不同的盐值,生成的签名或者序列化的数值不一样

    >>> s1 = URLSafeSerializer('secret-key', salt='activate-salt')
    >>> s1.dumps(42)
    'NDI.kubVFOOugP5PAIfEqLJbXQbfTxs'
    >>> s2 = URLSafeSerializer('secret-key', salt='upgrade-salt')
    >>> s2.dumps(42)
    'NDI.7lx-N1P-z2veJ7nT1_2bnTkjGTE'
    >>> s2.loads(s1.dumps(42))
    Traceback (most recent call last):
      ...
    itsdangerous.BadSignature: Signature "kubVFOOugP5PAIfEqLJbXQbfTxs" does not match
    Only the serializer with the same salt can load the value:
    
    >>> s2.loads(s2.dumps(42))
    42

    九、源码奉上:

      1 # -*- coding: utf-8 -*-
      2 """
      3     itsdangerous
      4     ~~~~~~~~~~~~
      5 
      6     A module that implements various functions to deal with untrusted
      7     sources.  Mainly useful for web applications.
      8 
      9     :copyright: (c) 2014 by Armin Ronacher and the Django Software Foundation.
     10     :license: BSD, see LICENSE for more details.
     11 """
     12 
     13 import sys
     14 import hmac
     15 import zlib
     16 import time
     17 import base64
     18 import hashlib
     19 import operator
     20 from datetime import datetime
     21 
     22 
     23 PY2 = sys.version_info[0] == 2
     24 if PY2:
     25     from itertools import izip
     26     text_type = unicode
     27     int_to_byte = chr
     28     number_types = (int, long, float)
     29 else:
     30     from functools import reduce
     31     izip = zip
     32     text_type = str
     33     int_to_byte = operator.methodcaller('to_bytes', 1, 'big')
     34     number_types = (int, float)
     35 
     36 
     37 try:
     38     import simplejson as json
     39 except ImportError:
     40     import json
     41 
     42 
     43 class _CompactJSON(object):
     44     """Wrapper around simplejson that strips whitespace.
     45     """
     46 
     47     def loads(self, payload):
     48         return json.loads(payload)
     49 
     50     def dumps(self, obj):
     51         return json.dumps(obj, separators=(',', ':'))
     52 
     53 
     54 compact_json = _CompactJSON()
     55 
     56 
     57 # 2011/01/01 in UTC
     58 EPOCH = 1293840000
     59 
     60 
     61 def want_bytes(s, encoding='utf-8', errors='strict'):
     62     if isinstance(s, text_type):
     63         s = s.encode(encoding, errors)
     64     return s
     65 
     66 
     67 def is_text_serializer(serializer):
     68     """Checks wheather a serializer generates text or binary."""
     69     return isinstance(serializer.dumps({}), text_type)
     70 
     71 
     72 # Starting with 3.3 the standard library has a c-implementation for
     73 # constant time string compares.
     74 _builtin_constant_time_compare = getattr(hmac, 'compare_digest', None)
     75 
     76 
     77 def constant_time_compare(val1, val2):
     78     """Returns True if the two strings are equal, False otherwise.
     79 
     80     The time taken is independent of the number of characters that match.  Do
     81     not use this function for anything else than comparision with known
     82     length targets.
     83 
     84     This is should be implemented in C in order to get it completely right.
     85     """
     86     if _builtin_constant_time_compare is not None:
     87         return _builtin_constant_time_compare(val1, val2)
     88     len_eq = len(val1) == len(val2)
     89     if len_eq:
     90         result = 0
     91         left = val1
     92     else:
     93         result = 1
     94         left = val2
     95     for x, y in izip(bytearray(left), bytearray(val2)):
     96         result |= x ^ y
     97     return result == 0
     98 
     99 
    100 class BadData(Exception):
    101     """Raised if bad data of any sort was encountered.  This is the
    102     base for all exceptions that itsdangerous is currently using.
    103 
    104     .. versionadded:: 0.15
    105     """
    106     message = None
    107 
    108     def __init__(self, message):
    109         Exception.__init__(self, message)
    110         self.message = message
    111 
    112     def __str__(self):
    113         return text_type(self.message)
    114 
    115     if PY2:
    116         __unicode__ = __str__
    117         def __str__(self):
    118             return self.__unicode__().encode('utf-8')
    119 
    120 
    121 class BadPayload(BadData):
    122     """This error is raised in situations when payload is loaded without
    123     checking the signature first and an exception happend as a result of
    124     that.  The original exception that caused that will be stored on the
    125     exception as :attr:`original_error`.
    126 
    127     This can also happen with a :class:`JSONWebSignatureSerializer` that
    128     is subclassed and uses a different serializer for the payload than
    129     the expected one.
    130 
    131     .. versionadded:: 0.15
    132     """
    133 
    134     def __init__(self, message, original_error=None):
    135         BadData.__init__(self, message)
    136         #: If available, the error that indicates why the payload
    137         #: was not valid.  This might be `None`.
    138         self.original_error = original_error
    139 
    140 
    141 class BadSignature(BadData):
    142     """This error is raised if a signature does not match.  As of
    143     itsdangerous 0.14 there are helpful attributes on the exception
    144     instances.  You can also catch down the baseclass :exc:`BadData`.
    145     """
    146 
    147     def __init__(self, message, payload=None):
    148         BadData.__init__(self, message)
    149         #: The payload that failed the signature test.  In some
    150         #: situations you might still want to inspect this, even if
    151         #: you know it was tampered with.
    152         #:
    153         #: .. versionadded:: 0.14
    154         self.payload = payload
    155 
    156 
    157 class BadTimeSignature(BadSignature):
    158     """Raised for time based signatures that fail.  This is a subclass
    159     of :class:`BadSignature` so you can catch those down as well.
    160     """
    161 
    162     def __init__(self, message, payload=None, date_signed=None):
    163         BadSignature.__init__(self, message, payload)
    164 
    165         #: If the signature expired this exposes the date of when the
    166         #: signature was created.  This can be helpful in order to
    167         #: tell the user how long a link has been gone stale.
    168         #:
    169         #: .. versionadded:: 0.14
    170         self.date_signed = date_signed
    171 
    172 
    173 class BadHeader(BadSignature):
    174     """Raised if a signed header is invalid in some form.  This only
    175     happens for serializers that have a header that goes with the
    176     signature.
    177 
    178     .. versionadded:: 0.24
    179     """
    180 
    181     def __init__(self, message, payload=None, header=None,
    182                  original_error=None):
    183         BadSignature.__init__(self, message, payload)
    184 
    185         #: If the header is actually available but just malformed it
    186         #: might be stored here.
    187         self.header = header
    188 
    189         #: If available, the error that indicates why the payload
    190         #: was not valid.  This might be `None`.
    191         self.original_error = original_error
    192 
    193 
    194 class SignatureExpired(BadTimeSignature):
    195     """Signature timestamp is older than required max_age.  This is a
    196     subclass of :exc:`BadTimeSignature` so you can use the baseclass for
    197     catching the error.
    198     """
    199 
    200 
    201 def base64_encode(string):
    202     """base64 encodes a single bytestring (and is tolerant to getting
    203     called with a unicode string).
    204     The resulting bytestring is safe for putting into URLs.
    205     """
    206     string = want_bytes(string)
    207     return base64.urlsafe_b64encode(string).strip(b'=')
    208 
    209 
    210 def base64_decode(string):
    211     """base64 decodes a single bytestring (and is tolerant to getting
    212     called with a unicode string).
    213     The result is also a bytestring.
    214     """
    215     string = want_bytes(string, encoding='ascii', errors='ignore')
    216     return base64.urlsafe_b64decode(string + b'=' * (-len(string) % 4))
    217 
    218 
    219 def int_to_bytes(num):
    220     assert num >= 0
    221     rv = []
    222     while num:
    223         rv.append(int_to_byte(num & 0xff))
    224         num >>= 8
    225     return b''.join(reversed(rv))
    226 
    227 
    228 def bytes_to_int(bytestr):
    229     return reduce(lambda a, b: a << 8 | b, bytearray(bytestr), 0)
    230 
    231 
    232 class SigningAlgorithm(object):
    233     """Subclasses of `SigningAlgorithm` have to implement `get_signature` to
    234     provide signature generation functionality.
    235     """
    236 
    237     def get_signature(self, key, value):
    238         """Returns the signature for the given key and value"""
    239         raise NotImplementedError()
    240 
    241     def verify_signature(self, key, value, sig):
    242         """Verifies the given signature matches the expected signature"""
    243         return constant_time_compare(sig, self.get_signature(key, value))
    244 
    245 
    246 class NoneAlgorithm(SigningAlgorithm):
    247     """This class provides a algorithm that does not perform any signing and
    248     returns an empty signature.
    249     """
    250 
    251     def get_signature(self, key, value):
    252         return b''
    253 
    254 
    255 class HMACAlgorithm(SigningAlgorithm):
    256     """This class provides signature generation using HMACs."""
    257 
    258     #: The digest method to use with the MAC algorithm.  This defaults to sha1
    259     #: but can be changed for any other function in the hashlib module.
    260     default_digest_method = staticmethod(hashlib.sha1)
    261 
    262     def __init__(self, digest_method=None):
    263         if digest_method is None:
    264             digest_method = self.default_digest_method
    265         self.digest_method = digest_method
    266 
    267     def get_signature(self, key, value):
    268         mac = hmac.new(key, msg=value, digestmod=self.digest_method)
    269         return mac.digest()
    270 
    271 
    272 class Signer(object):
    273     """This class can sign bytes and unsign it and validate the signature
    274     provided.
    275 
    276     Salt can be used to namespace the hash, so that a signed string is only
    277     valid for a given namespace.  Leaving this at the default value or re-using
    278     a salt value across different parts of your application where the same
    279     signed value in one part can mean something different in another part
    280     is a security risk.
    281 
    282     See :ref:`the-salt` for an example of what the salt is doing and how you
    283     can utilize it.
    284 
    285     .. versionadded:: 0.14
    286        `key_derivation` and `digest_method` were added as arguments to the
    287        class constructor.
    288 
    289     .. versionadded:: 0.18
    290         `algorithm` was added as an argument to the class constructor.
    291     """
    292 
    293     #: The digest method to use for the signer.  This defaults to sha1 but can
    294     #: be changed for any other function in the hashlib module.
    295     #:
    296     #: .. versionchanged:: 0.14
    297     default_digest_method = staticmethod(hashlib.sha1)
    298 
    299     #: Controls how the key is derived.  The default is Django style
    300     #: concatenation.  Possible values are ``concat``, ``django-concat``
    301     #: and ``hmac``.  This is used for deriving a key from the secret key
    302     #: with an added salt.
    303     #:
    304     #: .. versionadded:: 0.14
    305     default_key_derivation = 'django-concat'
    306 
    307     def __init__(self, secret_key, salt=None, sep='.', key_derivation=None,
    308                  digest_method=None, algorithm=None):
    309         self.secret_key = want_bytes(secret_key)
    310         self.sep = sep
    311         self.salt = 'itsdangerous.Signer' if salt is None else salt
    312         if key_derivation is None:
    313             key_derivation = self.default_key_derivation
    314         self.key_derivation = key_derivation
    315         if digest_method is None:
    316             digest_method = self.default_digest_method
    317         self.digest_method = digest_method
    318         if algorithm is None:
    319             algorithm = HMACAlgorithm(self.digest_method)
    320         self.algorithm = algorithm
    321 
    322     def derive_key(self):
    323         """This method is called to derive the key.  If you're unhappy with
    324         the default key derivation choices you can override them here.
    325         Keep in mind that the key derivation in itsdangerous is not intended
    326         to be used as a security method to make a complex key out of a short
    327         password.  Instead you should use large random secret keys.
    328         """
    329         salt = want_bytes(self.salt)
    330         if self.key_derivation == 'concat':
    331             return self.digest_method(salt + self.secret_key).digest()
    332         elif self.key_derivation == 'django-concat':
    333             return self.digest_method(salt + b'signer' +
    334                 self.secret_key).digest()
    335         elif self.key_derivation == 'hmac':
    336             mac = hmac.new(self.secret_key, digestmod=self.digest_method)
    337             mac.update(salt)
    338             return mac.digest()
    339         elif self.key_derivation == 'none':
    340             return self.secret_key
    341         else:
    342             raise TypeError('Unknown key derivation method')
    343 
    344     def get_signature(self, value):
    345         """Returns the signature for the given value"""
    346         value = want_bytes(value)
    347         key = self.derive_key()
    348         sig = self.algorithm.get_signature(key, value)
    349         return base64_encode(sig)
    350 
    351     def sign(self, value):
    352         """Signs the given string."""
    353         return value + want_bytes(self.sep) + self.get_signature(value)
    354 
    355     def verify_signature(self, value, sig):
    356         """Verifies the signature for the given value."""
    357         key = self.derive_key()
    358         try:
    359             sig = base64_decode(sig)
    360         except Exception:
    361             return False
    362         return self.algorithm.verify_signature(key, value, sig)
    363 
    364     def unsign(self, signed_value):
    365         """Unsigns the given string."""
    366         signed_value = want_bytes(signed_value)
    367         sep = want_bytes(self.sep)
    368         if sep not in signed_value:
    369             raise BadSignature('No %r found in value' % self.sep)
    370         value, sig = signed_value.rsplit(sep, 1)
    371         if self.verify_signature(value, sig):
    372             return value
    373         raise BadSignature('Signature %r does not match' % sig,
    374                            payload=value)
    375 
    376     def validate(self, signed_value):
    377         """Just validates the given signed value.  Returns `True` if the
    378         signature exists and is valid, `False` otherwise."""
    379         try:
    380             self.unsign(signed_value)
    381             return True
    382         except BadSignature:
    383             return False
    384 
    385 
    386 class TimestampSigner(Signer):
    387     """Works like the regular :class:`Signer` but also records the time
    388     of the signing and can be used to expire signatures.  The unsign
    389     method can rause a :exc:`SignatureExpired` method if the unsigning
    390     failed because the signature is expired.  This exception is a subclass
    391     of :exc:`BadSignature`.
    392     """
    393 
    394     def get_timestamp(self):
    395         """Returns the current timestamp.  This implementation returns the
    396         seconds since 1/1/2011.  The function must return an integer.
    397         """
    398         return int(time.time() - EPOCH)
    399 
    400     def timestamp_to_datetime(self, ts):
    401         """Used to convert the timestamp from `get_timestamp` into a
    402         datetime object.
    403         """
    404         return datetime.utcfromtimestamp(ts + EPOCH)
    405 
    406     def sign(self, value):
    407         """Signs the given string and also attaches a time information."""
    408         value = want_bytes(value)
    409         timestamp = base64_encode(int_to_bytes(self.get_timestamp()))
    410         sep = want_bytes(self.sep)
    411         value = value + sep + timestamp
    412         return value + sep + self.get_signature(value)
    413 
    414     def unsign(self, value, max_age=None, return_timestamp=False):
    415         """Works like the regular :meth:`~Signer.unsign` but can also
    416         validate the time.  See the base docstring of the class for
    417         the general behavior.  If `return_timestamp` is set to `True`
    418         the timestamp of the signature will be returned as naive
    419         :class:`datetime.datetime` object in UTC.
    420         """
    421         try:
    422             result = Signer.unsign(self, value)
    423             sig_error = None
    424         except BadSignature as e:
    425             sig_error = e
    426             result = e.payload or b''
    427         sep = want_bytes(self.sep)
    428 
    429         # If there is no timestamp in the result there is something
    430         # seriously wrong.  In case there was a signature error, we raise
    431         # that one directly, otherwise we have a weird situation in which
    432         # we shouldn't have come except someone uses a time-based serializer
    433         # on non-timestamp data, so catch that.
    434         if not sep in result:
    435             if sig_error:
    436                 raise sig_error
    437             raise BadTimeSignature('timestamp missing', payload=result)
    438 
    439         value, timestamp = result.rsplit(sep, 1)
    440         try:
    441             timestamp = bytes_to_int(base64_decode(timestamp))
    442         except Exception:
    443             timestamp = None
    444 
    445         # Signature is *not* okay.  Raise a proper error now that we have
    446         # split the value and the timestamp.
    447         if sig_error is not None:
    448             raise BadTimeSignature(text_type(sig_error), payload=value,
    449                                    date_signed=timestamp)
    450 
    451         # Signature was okay but the timestamp is actually not there or
    452         # malformed.  Should not happen, but well.  We handle it nonetheless
    453         if timestamp is None:
    454             raise BadTimeSignature('Malformed timestamp', payload=value)
    455 
    456         # Check timestamp is not older than max_age
    457         if max_age is not None:
    458             age = self.get_timestamp() - timestamp
    459             if age > max_age:
    460                 raise SignatureExpired(
    461                     'Signature age %s > %s seconds' % (age, max_age),
    462                     payload=value,
    463                     date_signed=self.timestamp_to_datetime(timestamp))
    464 
    465         if return_timestamp:
    466             return value, self.timestamp_to_datetime(timestamp)
    467         return value
    468 
    469     def validate(self, signed_value, max_age=None):
    470         """Just validates the given signed value.  Returns `True` if the
    471         signature exists and is valid, `False` otherwise."""
    472         try:
    473             self.unsign(signed_value, max_age=max_age)
    474             return True
    475         except BadSignature:
    476             return False
    477 
    478 
    479 class Serializer(object):
    480     """This class provides a serialization interface on top of the
    481     signer.  It provides a similar API to json/pickle and other modules but is
    482     slightly differently structured internally.  If you want to change the
    483     underlying implementation for parsing and loading you have to override the
    484     :meth:`load_payload` and :meth:`dump_payload` functions.
    485 
    486     This implementation uses simplejson if available for dumping and loading
    487     and will fall back to the standard library's json module if it's not
    488     available.
    489 
    490     Starting with 0.14 you do not need to subclass this class in order to
    491     switch out or customer the :class:`Signer`.  You can instead also pass a
    492     different class to the constructor as well as keyword arguments as
    493     dictionary that should be forwarded::
    494 
    495         s = Serializer(signer_kwargs={'key_derivation': 'hmac'})
    496 
    497     .. versionchanged:: 0.14:
    498        The `signer` and `signer_kwargs` parameters were added to the
    499        constructor.
    500     """
    501 
    502     #: If a serializer module or class is not passed to the constructor
    503     #: this one is picked up.  This currently defaults to :mod:`json`.
    504     default_serializer = json
    505 
    506     #: The default :class:`Signer` class that is being used by this
    507     #: serializer.
    508     #:
    509     #: .. versionadded:: 0.14
    510     default_signer = Signer
    511 
    512     def __init__(self, secret_key, salt=b'itsdangerous', serializer=None,
    513                  signer=None, signer_kwargs=None):
    514         self.secret_key = want_bytes(secret_key)
    515         self.salt = want_bytes(salt)
    516         if serializer is None:
    517             serializer = self.default_serializer
    518         self.serializer = serializer
    519         self.is_text_serializer = is_text_serializer(serializer)
    520         if signer is None:
    521             signer = self.default_signer
    522         self.signer = signer
    523         self.signer_kwargs = signer_kwargs or {}
    524 
    525     def load_payload(self, payload, serializer=None):
    526         """Loads the encoded object.  This function raises :class:`BadPayload`
    527         if the payload is not valid.  The `serializer` parameter can be used to
    528         override the serializer stored on the class.  The encoded payload is
    529         always byte based.
    530         """
    531         if serializer is None:
    532             serializer = self.serializer
    533             is_text = self.is_text_serializer
    534         else:
    535             is_text = is_text_serializer(serializer)
    536         try:
    537             if is_text:
    538                 payload = payload.decode('utf-8')
    539             return serializer.loads(payload)
    540         except Exception as e:
    541             raise BadPayload('Could not load the payload because an '
    542                 'exception occurred on unserializing the data',
    543                 original_error=e)
    544 
    545     def dump_payload(self, obj):
    546         """Dumps the encoded object.  The return value is always a
    547         bytestring.  If the internal serializer is text based the value
    548         will automatically be encoded to utf-8.
    549         """
    550         return want_bytes(self.serializer.dumps(obj))
    551 
    552     def make_signer(self, salt=None):
    553         """A method that creates a new instance of the signer to be used.
    554         The default implementation uses the :class:`Signer` baseclass.
    555         """
    556         if salt is None:
    557             salt = self.salt
    558         return self.signer(self.secret_key, salt=salt, **self.signer_kwargs)
    559 
    560     def dumps(self, obj, salt=None):
    561         """Returns a signed string serialized with the internal serializer.
    562         The return value can be either a byte or unicode string depending
    563         on the format of the internal serializer.
    564         """
    565         payload = want_bytes(self.dump_payload(obj))
    566         rv = self.make_signer(salt).sign(payload)
    567         if self.is_text_serializer:
    568             rv = rv.decode('utf-8')
    569         return rv
    570 
    571     def dump(self, obj, f, salt=None):
    572         """Like :meth:`dumps` but dumps into a file.  The file handle has
    573         to be compatible with what the internal serializer expects.
    574         """
    575         f.write(self.dumps(obj, salt))
    576 
    577     def loads(self, s, salt=None):
    578         """Reverse of :meth:`dumps`, raises :exc:`BadSignature` if the
    579         signature validation fails.
    580         """
    581         s = want_bytes(s)
    582         return self.load_payload(self.make_signer(salt).unsign(s))
    583 
    584     def load(self, f, salt=None):
    585         """Like :meth:`loads` but loads from a file."""
    586         return self.loads(f.read(), salt)
    587 
    588     def loads_unsafe(self, s, salt=None):
    589         """Like :meth:`loads` but without verifying the signature.  This is
    590         potentially very dangerous to use depending on how your serializer
    591         works.  The return value is ``(signature_okay, payload)`` instead of
    592         just the payload.  The first item will be a boolean that indicates
    593         if the signature is okay (``True``) or if it failed.  This function
    594         never fails.
    595 
    596         Use it for debugging only and if you know that your serializer module
    597         is not exploitable (eg: do not use it with a pickle serializer).
    598 
    599         .. versionadded:: 0.15
    600         """
    601         return self._loads_unsafe_impl(s, salt)
    602 
    603     def _loads_unsafe_impl(self, s, salt, load_kwargs=None,
    604                            load_payload_kwargs=None):
    605         """Lowlevel helper function to implement :meth:`loads_unsafe` in
    606         serializer subclasses.
    607         """
    608         try:
    609             return True, self.loads(s, salt=salt, **(load_kwargs or {}))
    610         except BadSignature as e:
    611             if e.payload is None:
    612                 return False, None
    613             try:
    614                 return False, self.load_payload(e.payload,
    615                     **(load_payload_kwargs or {}))
    616             except BadPayload:
    617                 return False, None
    618 
    619     def load_unsafe(self, f, *args, **kwargs):
    620         """Like :meth:`loads_unsafe` but loads from a file.
    621 
    622         .. versionadded:: 0.15
    623         """
    624         return self.loads_unsafe(f.read(), *args, **kwargs)
    625 
    626 
    627 class TimedSerializer(Serializer):
    628     """Uses the :class:`TimestampSigner` instead of the default
    629     :meth:`Signer`.
    630     """
    631 
    632     default_signer = TimestampSigner
    633 
    634     def loads(self, s, max_age=None, return_timestamp=False, salt=None):
    635         """Reverse of :meth:`dumps`, raises :exc:`BadSignature` if the
    636         signature validation fails.  If a `max_age` is provided it will
    637         ensure the signature is not older than that time in seconds.  In
    638         case the signature is outdated, :exc:`SignatureExpired` is raised
    639         which is a subclass of :exc:`BadSignature`.  All arguments are
    640         forwarded to the signer's :meth:`~TimestampSigner.unsign` method.
    641         """
    642         base64d, timestamp = self.make_signer(salt) 
    643             .unsign(s, max_age, return_timestamp=True)
    644         payload = self.load_payload(base64d)
    645         if return_timestamp:
    646             return payload, timestamp
    647         return payload
    648 
    649     def loads_unsafe(self, s, max_age=None, salt=None):
    650         load_kwargs = {'max_age': max_age}
    651         load_payload_kwargs = {}
    652         return self._loads_unsafe_impl(s, salt, load_kwargs, load_payload_kwargs)
    653 
    654 
    655 class JSONWebSignatureSerializer(Serializer):
    656     """This serializer implements JSON Web Signature (JWS) support.  Only
    657     supports the JWS Compact Serialization.
    658     """
    659 
    660     jws_algorithms = {
    661         'HS256': HMACAlgorithm(hashlib.sha256),
    662         'HS384': HMACAlgorithm(hashlib.sha384),
    663         'HS512': HMACAlgorithm(hashlib.sha512),
    664         'none': NoneAlgorithm(),
    665     }
    666 
    667     #: The default algorithm to use for signature generation
    668     default_algorithm = 'HS256'
    669 
    670     default_serializer = compact_json
    671 
    672     def __init__(self, secret_key, salt=None, serializer=None,
    673                  signer=None, signer_kwargs=None, algorithm_name=None):
    674         Serializer.__init__(self, secret_key, salt, serializer,
    675                             signer, signer_kwargs)
    676         if algorithm_name is None:
    677             algorithm_name = self.default_algorithm
    678         self.algorithm_name = algorithm_name
    679         self.algorithm = self.make_algorithm(algorithm_name)
    680 
    681     def load_payload(self, payload, return_header=False):
    682         payload = want_bytes(payload)
    683         if b'.' not in payload:
    684             raise BadPayload('No "." found in value')
    685         base64d_header, base64d_payload = payload.split(b'.', 1)
    686         try:
    687             json_header = base64_decode(base64d_header)
    688         except Exception as e:
    689             raise BadHeader('Could not base64 decode the header because of '
    690                 'an exception', original_error=e)
    691         try:
    692             json_payload = base64_decode(base64d_payload)
    693         except Exception as e:
    694             raise BadPayload('Could not base64 decode the payload because of '
    695                 'an exception', original_error=e)
    696         try:
    697             header = Serializer.load_payload(self, json_header,
    698                 serializer=json)
    699         except BadData as e:
    700             raise BadHeader('Could not unserialize header because it was '
    701                 'malformed', original_error=e)
    702         if not isinstance(header, dict):
    703             raise BadHeader('Header payload is not a JSON object',
    704                 header=header)
    705         payload = Serializer.load_payload(self, json_payload)
    706         if return_header:
    707             return payload, header
    708         return payload
    709 
    710     def dump_payload(self, header, obj):
    711         base64d_header = base64_encode(self.serializer.dumps(header))
    712         base64d_payload = base64_encode(self.serializer.dumps(obj))
    713         return base64d_header + b'.' + base64d_payload
    714 
    715     def make_algorithm(self, algorithm_name):
    716         try:
    717             return self.jws_algorithms[algorithm_name]
    718         except KeyError:
    719             raise NotImplementedError('Algorithm not supported')
    720 
    721     def make_signer(self, salt=None, algorithm=None):
    722         if salt is None:
    723             salt = self.salt
    724         key_derivation = 'none' if salt is None else None
    725         if algorithm is None:
    726             algorithm = self.algorithm
    727         return self.signer(self.secret_key, salt=salt, sep='.',
    728             key_derivation=key_derivation, algorithm=algorithm)
    729 
    730     def make_header(self, header_fields):
    731         header = header_fields.copy() if header_fields else {}
    732         header['alg'] = self.algorithm_name
    733         return header
    734 
    735     def dumps(self, obj, salt=None, header_fields=None):
    736         """Like :meth:`~Serializer.dumps` but creates a JSON Web Signature.  It
    737         also allows for specifying additional fields to be included in the JWS
    738         Header.
    739         """
    740         header = self.make_header(header_fields)
    741         signer = self.make_signer(salt, self.algorithm)
    742         return signer.sign(self.dump_payload(header, obj))
    743 
    744     def loads(self, s, salt=None, return_header=False):
    745         """Reverse of :meth:`dumps`. If requested via `return_header` it will
    746         return a tuple of payload and header.
    747         """
    748         payload, header = self.load_payload(
    749             self.make_signer(salt, self.algorithm).unsign(want_bytes(s)),
    750             return_header=True)
    751         if header.get('alg') != self.algorithm_name:
    752             raise BadHeader('Algorithm mismatch', header=header,
    753                             payload=payload)
    754         if return_header:
    755             return payload, header
    756         return payload
    757 
    758     def loads_unsafe(self, s, salt=None, return_header=False):
    759         kwargs = {'return_header': return_header}
    760         return self._loads_unsafe_impl(s, salt, kwargs, kwargs)
    761 
    762 
    763 class TimedJSONWebSignatureSerializer(JSONWebSignatureSerializer):
    764     """Works like the regular :class:`JSONWebSignatureSerializer` but also
    765     records the time of the signing and can be used to expire signatures.
    766 
    767     JWS currently does not specify this behavior but it mentions a possibility
    768     extension like this in the spec.  Expiry date is encoded into the header
    769     similarily as specified in `draft-ietf-oauth-json-web-token
    770     <http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#expDef`_.
    771 
    772     The unsign method can raise a :exc:`SignatureExpired` method if the
    773     unsigning failed because the signature is expired.  This exception is a
    774     subclass of :exc:`BadSignature`.
    775     """
    776 
    777     DEFAULT_EXPIRES_IN = 3600
    778 
    779     def __init__(self, secret_key, expires_in=None, **kwargs):
    780         JSONWebSignatureSerializer.__init__(self, secret_key, **kwargs)
    781         if expires_in is None:
    782             expires_in = self.DEFAULT_EXPIRES_IN
    783         self.expires_in = expires_in
    784 
    785     def make_header(self, header_fields):
    786         header = JSONWebSignatureSerializer.make_header(self, header_fields)
    787         iat = self.now()
    788         exp = iat + self.expires_in
    789         header['iat'] = iat
    790         header['exp'] = exp
    791         return header
    792 
    793     def loads(self, s, salt=None, return_header=False):
    794         payload, header = JSONWebSignatureSerializer.loads(
    795             self, s, salt, return_header=True)
    796 
    797         if 'exp' not in header:
    798             raise BadSignature('Missing expiry date', payload=payload)
    799 
    800         if not (isinstance(header['exp'], number_types)
    801                 and header['exp'] > 0):
    802             raise BadSignature('expiry date is not an IntDate',
    803                                payload=payload)
    804 
    805         if header['exp'] < self.now():
    806             raise SignatureExpired('Signature expired', payload=payload,
    807                                    date_signed=self.get_issue_date(header))
    808 
    809         if return_header:
    810             return payload, header
    811         return payload
    812 
    813     def get_issue_date(self, header):
    814         rv = header.get('iat')
    815         if isinstance(rv, number_types):
    816             return datetime.utcfromtimestamp(int(rv))
    817 
    818     def now(self):
    819         return int(time.time())
    820 
    821 
    822 class URLSafeSerializerMixin(object):
    823     """Mixed in with a regular serializer it will attempt to zlib compress
    824     the string to make it shorter if necessary.  It will also base64 encode
    825     the string so that it can safely be placed in a URL.
    826     """
    827 
    828     def load_payload(self, payload):
    829         decompress = False
    830         if payload.startswith(b'.'):
    831             payload = payload[1:]
    832             decompress = True
    833         try:
    834             json = base64_decode(payload)
    835         except Exception as e:
    836             raise BadPayload('Could not base64 decode the payload because of '
    837                 'an exception', original_error=e)
    838         if decompress:
    839             try:
    840                 json = zlib.decompress(json)
    841             except Exception as e:
    842                 raise BadPayload('Could not zlib decompress the payload before '
    843                     'decoding the payload', original_error=e)
    844         return super(URLSafeSerializerMixin, self).load_payload(json)
    845 
    846     def dump_payload(self, obj):
    847         json = super(URLSafeSerializerMixin, self).dump_payload(obj)
    848         is_compressed = False
    849         compressed = zlib.compress(json)
    850         if len(compressed) < (len(json) - 1):
    851             json = compressed
    852             is_compressed = True
    853         base64d = base64_encode(json)
    854         if is_compressed:
    855             base64d = b'.' + base64d
    856         return base64d
    857 
    858 
    859 class URLSafeSerializer(URLSafeSerializerMixin, Serializer):
    860     """Works like :class:`Serializer` but dumps and loads into a URL
    861     safe string consisting of the upper and lowercase character of the
    862     alphabet as well as ``'_'``, ``'-'`` and ``'.'``.
    863     """
    864     default_serializer = compact_json
    865 
    866 
    867 class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer):
    868     """Works like :class:`TimedSerializer` but dumps and loads into a URL
    869     safe string consisting of the upper and lowercase character of the
    870     alphabet as well as ``'_'``, ``'-'`` and ``'.'``.
    871     """
    872     default_serializer = compact_json
    itsdangerous.py

    refer:

    1、https://pythonhosted.org/itsdangerous/

    2、http://itsdangerous.readthedocs.io/en/latest/

    3、http://cxymrzero.github.io/blog/2015/03/18/flask-token/

  • 相关阅读:
    asp.net 中的viewstate用法?
    .net中 过滤 指定 字符串
    js中replace的用法
    restart
    外部函数
    JQuery实现Ajax 根据商品名称自动显示价格
    ListView中命令行按钮应用;
    GridView中获取行数和列数
    全局应用程序类Global
    如何获取gridview中模板列中控件的值?
  • 原文地址:https://www.cnblogs.com/shengulong/p/7475537.html
Copyright © 2020-2023  润新知