专栏首页马一特关于Cookie和Session

关于Cookie和Session

简单的说

  • Cookie是保存在浏览器的键值对
  • Session是保存在服务端的键值对
  • Session是依赖于Cookie的

在Django框架中,我们可以直接操作cookie和session,但是tornado只支持cookie,那如果要使用session怎么办呢?自己定义session

基于内存实现SESSION

我们知道,在tornado中,所有的请求都是由RequestHandler对象来处理(以下简称handler对象)。在RequestHandler源码中,预留了一个钩子方法initialize,该方法会在实例化Handler对象时执行。因此,如果我们继承RequestHandler类并重写initialize,就可以完成一些自定义操作。  

import os
import tornado.ioloop
import tornado.web
from tornado.web import RequestHandler
import hashlib
import time
# 生成一个随机的字符串
def gen_random_str():
    md5 = hashlib.md5()
    md5.update(str(time.time()).encode('utf-8'))
    return md5.hexdigest()
class CacheSession(object):
    container = {}
    def __init__(self,handler):
        self.handler = handler
        client_random_str = self.handler.get_cookie("_session_id_")
        if client_random_str and client_random_str in self.container:
            self.random_str = client_random_str
        else:
            self.random_str = gen_random_str()
            self.container[self.random_str] = {}
        expires = time.time() + 300
        self.handler.set_cookie("_session_id_", self.random_str, expires=expires)
    def __setitem__(self, key, value):
        self.container[self.random_str][key] = value
    def __getitem__(self, item):
        return self.container[self.random_str].get(item)
class SessionHandler(RequestHandler):
    def initialize(self):
        # self指代的是当前的对象 
        self.session = CacheSession(self)
class LoginHandler(SessionHandler,RequestHandler):
    def get(self, *args, **kwargs):
        self.render('login.html')
    def post(self, *args, **kwargs):
        username = self.get_argument('username')
        password = self.get_argument('password')
        if username == 'admin' and password == '123':
            self.session['username'] = username
            self.redirect('/main')
        else:
            self.redirect('/login')
class MainHandler(SessionHandler,RequestHandler):
    def get(self, *args, **kwargs):
        username = self.session['username']
        if username:
            self.write('this is main page')
        else:
            self.redirect('/login')
    def post(self, *args, **kwargs):
        pass
settings = {
    "static_path" : os.path.join(os.path.dirname(__file__),'static'),
    "static_url_prefix":"static",
    "template_path":'views',
    "xsrf_cookies": True,
}
def make_app():
    return tornado.web.Application([
        (r"/login", LoginHandler),
        (r"/main", MainHandler),
    ], **settings)
if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

代码解释

  • 定义一个Session类,其实例化时接收handler对象
    • Session类中定义一个静态字段(大字典),用来存储session_id和对应的用户信息;所有的session对象都可以访问这个大字典。
    • Session的构造方法中,获取和设置cookie
      • 调用handler对象get_cookie()方法获取session_id,如果没有,则生成一段随机字符串random_str作为session_id
      • session_id写入大字典
      • 调用handler对象的set_cookie()方法,通知浏览器设置cookie:set-cookie: {session_id: random_str}
    • Session类中,定义__getitem__, __setitem__, __delitem__方法来实现通过字典的方式操作session对象
  • initialize方法中为handler对象增加session属性,其值是Session对象:self.session=Session(self);在每个路由对应的视图中都重写initialize方法太麻烦了,利用面向对象的多继承,将这一步单独写在一个类SessionHandler,所以视图类先继承这个类即可
  • 每次请求进来,都会执行SessionHandler中的initialize方法,并实例化Session对象,从而获取session_id
  • 操作session
    • 通过self.session[key] = value , 即可调用session对象的__setitem__方法来写session
    • 通过self.session[key] 即可调用session对象的__getitem__方法来获取session
    • 通过del self.session[key] 即可调用session对象的__delitem__方法来删除session

基于Redis实现Session

import os
import tornado.ioloop
import tornado.web
from tornado.web import RequestHandler
import hashlib
import time
def gen_random_str():
    md5 = hashlib.md5()
    md5.update(str(time.time()).encode('utf-8'))
    return md5.hexdigest()
class RedisSession(object):
    @property
    def conn(self):
        import redis
        conn = redis.Redis(host='192.168.11.96', port=6379)
        return conn
    
    def __init__(self,handler):
        self.handler = handler
        client_random_str = self.handler.get_cookie("_session_id_")
        if client_random_str and self.conn.exists(client_random_str):
            self.random_str = client_random_str
        else:
            self.random_str = gen_random_str()
            
        expires = time.time() + 300
        self.handler.set_cookie("_session_id_", self.random_str, expires=expires)
        self.conn.expire(self.random_str, 300)
    def __setitem__(self, key, value):
        self.conn.hset(self.random_str, key, value)
    def __getitem__(self, item):
        return self.conn.hget(self.random_str, item)
class SessionHandler(RequestHandler):
    def initialize(self):
        self.session = RedisSession(self)

工厂模式实现Session

session_code.py

class SessionFactory(object):
    @staticmethod
    def get_session():
        import settings
        import importlib
        engine = settings.SESSION_ENGINE
        module_path,cls_name = engine.rsplit('.',maxsplit=1)
        md = importlib.import_module(module_path)
        cls = getattr(md,cls_name)
        return cls

app.py

from session_code import SessionFactory
cls = SessionFactory.get_session()
print(cls)

setting.py

SESSION_ENGINE = "session_code.RedisSession"
# SESSION_ENGINE = "session_code.CacheSession"
SESSION_ID = "__session__id__"
EXPIRERS = 300

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux: scp文件,目录上传下载标准版

    例如: 将服务器上 /server_path 路径下的 test.txt 文件 下载到本地 /local_path/local_dir 目录中

    马一特
  • Python爬虫抓取唐诗宋词

    Python语言的爬虫开发相对于其他编程语言是极其高效的,在上一篇文章 爬虫抓取博客园前10页标题带有Python关键字(不区分大小写) 的文章中,我们介绍了...

    马一特
  • ImportError: /lib64/libm.so.6: version `GLIBC_2.23' not found (required by /usr/local/python37/lib/p

    这个错误的出现往往与我前面的一篇文章 ImportError: /lib64/libm.so.6: version `CXXAB_1.3.8.' not fou...

    马一特
  • 接口测试 | 24 requests + unittest集成你的接口测试

    概述 本文就如何结合requests、unittest进行实例演示,如果你还不了解unittest、PO模型,请翻阅公众号前期发布的unittest专题和PO模...

    苦叶子
  • 接口测试 | 24 requests + unittest集成你的接口测试

    概述 本文就如何结合requests、unittest进行实例演示,如果你还不了解unittest、PO模型,请翻阅公众号前期发布的unittest专题和PO模...

    苦叶子
  • Python开发植物大战僵尸游戏

    ------------------- End -------------------

    python学习教程
  • VR+全景播放器+头控讲解-07

    酷走天涯
  • 数据结构(三):队列

      队列通常用来实现消息(任务)的快速读写,即消息队列。消息队列的常用来解决如下问题:

    py3study
  • Python开发植物大战僵尸游戏,详细教程

    一墨编程学习
  • Python 3 之 运算符重载详解

    实际上,“运算符重载”只是意味着在类方法中拦截内置的操作……当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果。...

    py3study

扫码关注云+社区

领取腾讯云代金券