在Tornado 1.0中,使用的是SHA1进行签名,而在Tornado 4.0中的新版本中使用的是SHA256进行签名,同时输出的Cookie格式有差异——开始第一个数字是版本号(secure cookie使用的version,而非Tornado的版本)。
新版本的set_secure_cookie设置Cookie的相关源码如下:
代码如下 | 复制代码 |
## Tornado 4.0中web.py的部分源码 ## set_secure_cookie相关的源码 MIN_SUPPORTED_SIGNED_VALUE_VERSION = 1 MAX_SUPPORTED_SIGNED_VALUE_VERSION = 2 DEFAULT_SIGNED_VALUE_VERSION = 2 DEFAULT_SIGNED_VALUE_MIN_VERSION = 1 class RequestHandler(object): def set_cookie(self, name, value, domain=None, expires=None, path="/", expires_days=None, **kwargs): name = escape.native_str(name) value = escape.native_str(value) if re.search(r"[x00-x20]", name + value): raise ValueError("Invalid cookie %r: %r" % (name, value)) if not hasattr(self, "_new_cookie"): self._new_cookie = Cookie.SimpleCookie() if name in self._new_cookie: del self._new_cookie[name] self._new_cookie[name] = value morsel = self._new_cookie[name] if domain: morsel["domain"] = domain if expires_days is not None and not expires: expires = datetime.datetime.utcnow() + datetime.timedelta( days=expires_days) if expires: morsel["expires"] = httputil.format_timestamp(expires) if path: morsel["path"] = path for k, v in kwargs.items(): if k == 'max_age': k = 'max-age' morsel[k] = v def set_secure_cookie(self, name, value, expires_days=30, version=None, **kwargs): self.set_cookie(name, self.create_signed_value(name, value, version=version), expires_days=expires_days, **kwargs) def create_signed_value(self, name, value, version=None): self.require_setting("cookie_secret", "secure cookies") return create_signed_value(self.application.settings["cookie_secret"], name, value, version=version) def create_signed_value(secret, name, value, version=None, clock=None): if version is None: version = DEFAULT_SIGNED_VALUE_VERSION if clock is None: clock = time.time timestamp = utf8(str(int(clock()))) value = base64.b64encode(utf8(value)) if version == 1: signature = _create_signature_v1(secret, name, value, timestamp) value = b"|".join([value, timestamp, signature]) return value elif version == 2: def format_field(s): return utf8("%d:" % len(s)) + utf8(s) to_sign = b"|".join([ b"2|1:0", format_field(timestamp), format_field(name), format_field(value), b'']) signature = _create_signature_v2(secret, to_sign) return to_sign + signature else: raise ValueError("Unsupported version %d" % version) def _create_signature_v1(secret, *parts): hash = hmac.new(utf8(secret), digestmod=hashlib.sha1) for part in parts: hash.update(utf8(part)) return utf8(hash.hexdigest()) def _create_signature_v2(secret, s): hash = hmac.new(utf8(secret), digestmod=hashlib.sha256) hash.update(utf8(s)) return utf8(hash.hexdigest()) |
调用的关系如下:
代码如下 | 复制代码 |
-------------+--------------------------+--------------------------------+-----------------------------------+---------------------------------+-------------------------- | | | | | | | | | | | | | | | | | | | | +-----------+---------+ | | | | | | | | | | | set_secure_cookie | | | | | | | | | | | +-----------+---------+ | | | | | | | | | | +-----------+-------------+ | | | | invoke | | | | | +------------->| create_signed_value | | | | | | | | | | | +-----------+-------------+ | | | | | | | | | | +--------------+-------------+ | | | | invoke | | | | | +---------------->| create_signed_value(global)| | | | | | | | | | | +--------------+-------------+ | | | | | | | | | | +-----------------+-------------+ | | | | invoke | | | | | +---------------->| _create_signature_v2(global) | | | | | | | | | | | +-----------------+-------------+ | | | | return signature string (sha256)| | | | return source and signature |<----------------------------------+ | | result |<-------------------------------+ | | |<-------------------------| string | | | | | | | | | | | | | | | | | +--------------+----------+ | | | | | | +-------------------------- -------------------------------- ----------------------------------- ----------------->| set_cookie | | | invoke set_cookie to set secure_cookie | | | | | | | +--------------+----------+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
需要注意,在RequestHandler类中的create_signed_value方法调用的是web.py中的模块方法create_signed_value。在获取到返回结果之后,set_secure_cookie调用set_cookie,将secure cookie放入cookie内。
版本2的secure cookie和版本1的签名区别就在_create_signature_v2和_create_signature_v1上。