在web開發的時候由於咱們常常須要用到cookie來識別和保存用戶狀態,可是cookie又不是很安全,不少信息直接明文就暴露了,這個時候session就派上用場了,而python web框架tornado就沒有自帶的session功能,這個時候我本身手擼一個出來,其實瞭解session的原理的話仍是很好寫的。python
什麼是session?git
咱們知道由於http鏈接是無狀態的,客戶端鏈接服務器請求完數據後便斷開鏈接,下一次鏈接的時候服務器端便不知道你是誰,那麼服務器要經過怎樣知道你是誰呢,這個時候就須要cookie了,cookie等因而一個身份令牌,當你來請求服務器端的時候,服務器端會給客戶端發一個令牌,而客戶端下一次訪問的時候,就能夠帶着這個令牌來訪問服務端。這就是cookie的原理。打個比方當你經過帳戶密碼登錄網站的時候,服務端就會往你的cookie裏面寫入,is_login=true, user_id=1這種方式來記錄用戶已經登錄。當你帶着這個 cookie去訪問服務端的時候,服務端就知道你已經登錄了,而且你是用戶1。可是這種方式太簡單粗暴了,不太安全。咱們應該保證這類令牌是隨機的不可預料性以保證其最基本的安全。web
瞭解cookie原理後再來看session原理,既然session能夠解決安全等問題,他是如何作到呢?既然cookie將一些敏感數據簡單粗暴的存儲在客戶端這樣不安全,那session就是將數據存儲在服務器端以便每次認證,並每次都會更新。json
照着這個思路咱們來看看代碼部分安全
這裏假設你已經完成了登錄功能,並建立了用戶表。服務器
首先在用戶模型中添加一個字段(個人tornado項目用的model模塊是peewee)cookie
sessions = peewee.CharField(db_column="sessions")
看看登錄是怎麼處理的session
# 假設前面完成用戶帳戶密碼認證並獲取到用戶對象member_obj(從用戶表中獲取到的用戶對象) # 生成10位的隨機字符串 sess_key = ''.join( random.choice(string.ascii_lowercase + string.digits) \ for i in range(10) ) # 將其保存到字典中 session = {"id": sess_key, "time": int(time.time())} try: # 從用戶表中讀取存入的json數據並將其從json轉爲列表 sessions = json.loads(member_obj.sessions) except: sessions = list() if not isinstance(sessions, list): sessions = list() # 將當前生成session key字典加入sessions list # 限制session list長度爲5 sessions.append(session) if len(sessions) > 5: sessions = sessions[-5:] 將session list轉爲json存入用戶表中的session列中 member_obj.sessions = json.dumps(sessions) member_obj.save() # 將其寫入cookie中,以便從cookie中獲取數據後對其進行認證(這裏其實暴露了member_id你能夠對cookie value二次加密,我偷懶就沒弄) self.set_cookie(self.settings["cookie_key_sess"], str(member_obj.member_id)+":"+sess_key )
而後咱們能夠對tornado的 RequestHandler的get_current_user進行重寫(這是tornado預留的鉤子函數,用戶用戶認證,詳細能夠看官方文檔)app
def get_current_user(self): cookie_data = self.get_cookie(self.settings["cookie_key_sess"]) if not cookie_data: return None try: telephone, session_id = cookie_data.split(":") except: return None #這個是我本身寫的Member model下的classmethod member = Member.get_user_by_sess(member_id, session_id) return member #Member model下的classmethod @classmethod def get_user_by_sess(cls, member_id, session_id): member = None sessions = None try: member = cls.get( (cls.member_id == member_id) & (cls.status == 'normal') #status是用戶狀態,判斷是否被註銷 ) sessions = json.loads(member.sessions) except: return None if not member or not sessions or not isinstance(sessions, list): return None for session in sessions: #判斷用戶攜帶過來的session數據是否有效,有效返回member對象 if isinstance(session, dict) and session.get("id") \ and session["id"] == session_id: return member return None