在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