我們將許多信息放在cookie
中勢必會造成瀏覽器端的臃腫, 此時便需要在服務端保存原本在瀏覽器端的那些鍵值對. 在瀏覽器端只需存儲一個表示身份的隨機加密字符串, 當瀏覽器端訪問服務端時候攜帶該字符串, 經(jīng)過比較, 驗證合法之后便可以取該用戶在服務端存儲的相應信息. 但是在Tornado
中并沒有session
的模塊, 我們需要自定義來實現(xiàn).
**all.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 存儲所有的用戶信息
ALL_USER_DIC = {}
**base.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web
from commons.session import Session
class BaseHandler(tornado.web.RequestHandler):
# 鉤子函數(shù), 子類初始化時候會將子類對象傳入該方法中執(zhí)行
def initialize(self):
# 這里將子類對象傳入session中, 則以后生成的session對象中就包含處理器的實例對象
self.session = Session(self)
**session.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from commons.all import ALL_USER_DIC
class Session:
def __init__(self, handler):
self.handler = handler
self.random_index_str = None
def __get_random_str(self):
import hashlib, time
# 生成md5對象
md = hashlib.md5()
# 加入自定義參數(shù)來更新md5對象
md.update(bytes(str(time.time()) + ' | own-secret', encoding='utf-8'))
# 得到加鹽后的十六進制隨機字符串來作為用戶的索引
return md.hexdigest()
def __setitem__(self, key, value):
# 當前session對象中沒有對應的索引的時候
if not self.random_index_str:
# 根據(jù)處理器對象獲得瀏覽器傳來的cookie的值
random_index_str = self.handler.get_secure_cookie("__sson__", None)
# 瀏覽器傳來的cookie的值為空的時候, 表示該用戶是第一次訪問本網(wǎng)站
if not random_index_str:
# 為當前的新用戶在當前的session對象中生成索引
self.random_index_str = self.__get_random_str()
# 為當前新用戶設置cookie
self.handler.set_secure_cookie('__sson__', self.random_index_str)
# 為當前用戶生成保存其相關內容的字典對象
ALL_USER_DIC[self.random_index_str] = {}
# 當瀏覽器傳來的cookie不為空的時候
else:
# 瀏覽器傳來的cookie非法的時候
if self.random_index_str not in ALL_USER_DIC.keys():
# 為當前非法用戶生產(chǎn)索引
self.random_index_str = self.__get_random_str()
# 僅僅為當前非法用戶生成其保存相關內容的字典對象, 避免合法老用戶的字典對象被清空
ALL_USER_DIC[self.random_index_str] = {}
# 不管當前session對象有沒有對應的索引都應該為他設置起相關的信息保存(當然了, 到這一步的時候經(jīng)過if條件語句的過濾, 剩下來的就是剛剛創(chuàng)建字典對象的新用戶或者非法用戶, 以及其他合法的老用戶了)
ALL_USER_DIC[self.random_index_str][key] = value
# 將為以上的新用戶或者非法用戶設置cookie的操作放在這里本無可厚非. 但是將老用戶的cookie也重新設置一遍, 其實是為老用戶更新過期時間而做的
self.handler.set_secure_cookie('__sson__', self.random_index_str)
def __getitem__(self, key):
# 獲取當前用戶cookie中保存的索引值, 注意加密方式返回的cookie的值是bytes類型的
self.random_index_str = self.handler.get_secure_cookie('__sson__', None)
# 若索引值為空表示當前用戶是新用戶, 則直接返回空, 程序到此終止
if not self.random_index_str:
return None
# 索引不為空的時候
else:
self.random_index_str = str(self.random_index_str, encoding="utf-8")
# 在服務器端為保存該索引值表示當前用戶是非法用戶,則直接返回空
current_user = ALL_USER_DIC.get(self.random_index_str, None)
if not current_user:
return None
else:
# 直接返回合法用戶指定的key的值, 沒有則默認返回空
return current_user.get(key, None)
**home.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web
from commons.base import BaseHandler
class IndexHandler(BaseHandler):
def get(self):
if self.get_argument('name', None) == 'test':
self.session['is_login'] = True
self.session['name'] = self.get_argument('name')
else:
self.write('登錄失敗, 請重新登錄!')
class AdminHandler(BaseHandler):
def get(self):
if self.session['is_login']:
self.write('歡迎%s回來. ' % (self.session['name'],))
else:
self.redirect('/index')
**start.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web, tornado.ioloop
from controllers import home
if __name__ == '__main__':
settings = {
# 模板路徑配置
'template_path': 'views',
"cookie_secret": 'test-secret,'
}
application = tornado.web.Application([
(r"/index", home.IndexHandler),
(r"/admin", home.AdminHandler),
], **settings)
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
- 至此我們就完成了基于加密
cookie
方式的session
服務端驗證 - 注意, 獲取加密方式的
cookie
的值是bytes
類型的, 一定要記得轉成str
類型, 否則用bytes
去找str
類型是無法匹配成功, 直接會造成即使登錄成功, 也無法訪問到admin
成功的頁面(筆者就曾掉進此坑了掙扎好久)