Cookie
對於RequestHandler,除了在第二章中講到的之外,還提供了操作cookie的方法。
設置
set_cookie(name, value, domain=None, expires=None, path='/', expires_days=None)
參數說明:
參數名 | 說明 |
---|---|
name | cookie名 |
value | cookie值 |
domain | 提交cookie時匹配的域名 |
path | 提交cookie時匹配的路徑 |
expires | cookie的有效期,可以是時間戳整數、時間元組或者datetime類型,爲UTC時間 |
expires_days | cookie的有效期,天數,優先級低於expires |
import datetime
class IndexHandler(RequestHandler):
def get(self):
self.set_cookie("n1", "v1")
self.set_cookie("n2", "v2", path="/new", expires=time.strptime("2016-11-11 23:59:59","%Y-%m-%d %H:%M:%S"))
self.set_cookie("n3", "v3", expires_days=20)
# 利用time.mktime將本地時間轉換爲UTC標準時間
self.set_cookie("n4", "v4", expires=time.mktime(time.strptime("2016-11-11 23:59:59","%Y-%m-%d %H:%M:%S")))
self.write("OK")
原理
設置cookie實際就是通過設置header的Set-Cookie來實現的。
class IndexHandler(RequestHandler):
def get(self):
self.set_header("Set-Cookie", "n5=v5; expires=Fri, 11 Nov 2016 15:59:59 GMT; Path=/")
self.write("OK")
獲取
get_cookie(name, default=None)
獲取名爲name的cookie,可以設置默認值。
class IndexHandler(RequestHandler):
def get(self):
n3 = self.get_cookie("n3")
self.write(n3)
清除
clear_cookie(name, path='/', domain=None)
刪除名爲name,並同時匹配domain和path的cookie。
clear_all_cookies(path='/', domain=None)
刪除同時匹配domain和path的所有cookie。
class ClearOneCookieHandler(RequestHandler):
def get(self):
self.clear_cookie("n3")
self.write("OK")
class ClearAllCookieHandler(RequestHandler):
def get(self):
self.clear_all_cookies()
self.write("OK")
注意:執行清除cookie操作後,並不是立即刪除了瀏覽器中的cookie,而是給cookie值置空,並改變其有效期使其失效。真正的刪除cookie是由瀏覽器去清理的。
安全Cookie
Cookie是存儲在客戶端瀏覽器中的,很容易被篡改。Tornado提供了一種對Cookie進行簡易加密簽名的方法來防止Cookie被惡意篡改。
使用安全Cookie需要爲應用配置一個用來給Cookie進行混淆的祕鑰cookie_secret,將其傳遞給Application的構造函數。我們可以使用如下方法來生成一個隨機字符串作爲cookie_secret的值。
>>> import base64, uuid
>>> base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
'2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A='
Base64是一種基於64個可打印字符來表示二進制數據的表示方法。由於2的6次方等於64,所以每6個比特爲一個單元,對應某個可打印字符。三個字節有24個比特,對應於4個Base64單元,即3個字節需要用4個可打印字符來表示。
uuid, 通用唯一識別碼(英語:Universally Unique Identifier,簡稱UUID),是由一組32個16進制數字所構成(兩個16進制數是一個字節,總共16字節),因此UUID理論上的總數爲16^32=2^128,約等於3.4 x 10^38。也就是說若每納秒產生1兆個UUID,要花100億年纔會將所有UUID用完。
uuid模塊的uuid4()函數可以隨機產生一個uuid碼,bytes屬性將此uuid碼作爲16字節字符串。
將生成的cookie_secret傳入Application構造函數:
app = tornado.web.Application(
[(r"/", IndexHandler),],
cookie_secret = "2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A="
)
獲取和設置
set_secure_cookie(name, value, expires_days=30)
設置一個帶簽名和時間戳的cookie,防止cookie被僞造。
get_secure_cookie(name, value=None, max_age_days=31)
如果cookie存在且驗證通過,返回cookie的值,否則返回None。max_age_day不同於expires_days,expires_days是設置瀏覽器中cookie的有效期,而max_age_day是過濾安全cookie的時間戳。
class IndexHandler(RequestHandler):
def get(self):
cookie = self.get_secure_cookie("count")
count = int(cookie) + 1 if cookie else 1
self.set_secure_cookie("count", str(count))
self.write(
'<html><head><title>Cookie計數器</title></head>'
'<body><h1>您已訪問本頁%d次。</h1>' % count +
'</body></html>'
)
我們看簽名後的cookie值:
"2|1:0|10:1476412069|5:count|4:NQ==|cb5fc1d4434971de6abf87270ac33381c686e4ec8c6f7e62130a0f8cbe5b7609"
字段說明:
- 安全cookie的版本,默認使用版本2,不帶長度說明前綴
- 默認爲0
- 時間戳
- cookie名
- base64編碼的cookie值
- 簽名值,不帶長度說明前綴
注意:Tornado的安全cookie只是一定程度的安全,僅僅是增加了惡意修改的難度。Tornado的安全cookies仍然容易被竊聽,而cookie值是簽名不是加密,攻擊者能夠讀取已存儲的cookie值,並且可以傳輸他們的數據到任意服務器,或者通過發送沒有修改的數據給應用僞造請求。因此,避免在瀏覽器cookie中存儲敏感的用戶數據是非常重要的。