自定义Tornado的session组件

session和cookie的关系

cookie:保存在客户端浏览器上的键值对
	session_id = "eyJ1c2VyX2luZm8iOiJ"

session:保存在服务器上的键值对
	{
		"eyJ1c2VyX2luZm8iOiJ":{'is_login':True, 'user':'standby',...},
		"iJhbGV4In0.DYUE4A.A":{'is_login':True, 'user':'alex',...},
		...
	}

- 用户第一次打开浏览器请求我的网站页面
	- 在服务器端生成一个随机字符串,作为value发给客户端浏览器。
	- 这个随机字符串在服务器的session中作为key,value={},保存起来。

 

知识准备

1. 通过给定字符串,如何实例化一个对象出来?

import importlib
path = "session_code.RedisSession"
md,cls = path.rsplit('.',maxsplit=1)
m = importlib.import_module(md)
cls = getattr(m,cls)
print(cls.__name__)

 或者

import importlib
path = "scrapy.middleware.MiddlewareManager"
tmp_li = path.split('.')
cls_name = tmp_li.pop()
prefix = '.'.join(tmp_li)
m = importlib.import_module(prefix)
cls = getattr(m,cls_name)
print(cls.__name__)

rest_framework/setting.py

def import_from_string(val, setting_name):
    """
    Attempt to import a class from a string representation.
    """
    try:
        # Nod to tastypie's use of importlib.
        module_path, class_name = val.rsplit('.', 1)
        module = import_module(module_path)
        return getattr(module, class_name)
    except (ImportError, AttributeError) as e:
        msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
        raise ImportError(msg)

from django.utils.module_loading import import_string

def import_string(dotted_path):
    """
    Import a dotted module path and return the attribute/class designated by the
    last name in the path. Raise ImportError if the import failed.
    """
    try:
        module_path, class_name = dotted_path.rsplit('.', 1)
    except ValueError:
        msg = "%s doesn't look like a module path" % dotted_path
        six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])

    module = import_module(module_path)

    try:
        return getattr(module, class_name)
    except AttributeError:
        msg = 'Module "%s" does not define a "%s" attribute/class' % (
            module_path, class_name)
        six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])

2. 面向对象里的 __setitem__ __getitem__ 

class Foo(object):
    def __getitem__(self, item):
        return "123"
    def __setitem__(self, key, value):
        self.__dict__[key] = value
    def __delitem__(self, key):
        print('----')
        self.__dict__.pop(key)

obj = Foo()
print(obj['k1'])    # __getitem__

obj['k1'] = 666     # __setitem__

del obj['k1']       # __delitem__



Django 里面操作session:
    request.session['xxx'] = xxxxx

3. 程序结合配置文件以及工厂模式

class MemSession(object):
    def __getitem__(self, item):
        return "123"

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('----')
        self.__dict__.pop(key)

class RedisSession(object):
    def __getitem__(self, item):
        return "123"

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('----')
        self.__dict__.pop(key)


class SessionFactory(object):
    """
        工厂模式
        settings.py :  SESSION_ENGINE = "session_code.RedisSession"
    """

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

  

Tornado基于内存和redis的session组件实现示例

app.py

from tornado import ioloop
from tornado.web import RequestHandler,Application
from session_code import SessionFactory

settings = {
    'template_path':'templates',
}

class SessionHandler(object):
    def initialize(self):
        cls = SessionFactory.get_session()
        self.session = cls(self)

class IndexHandler(SessionHandler,RequestHandler):

    def get(self):
        user = self.session['user']
        if user:
            self.write("首页欢迎你")
        else:
            self.redirect('/login')

class LoginHandler(SessionHandler,RequestHandler):

    def get(self):
        self.render('login.html',msg="")
    def post(self, *args, **kwargs):
        name = self.get_argument('name')
        pwd = self.get_argument('pwd')
        if 'alex'==name and '123' == pwd:
            self.session['user'] = name
            self.redirect('/index')
        else:
            self.render('login.html',msg="用户名或密码错误")

application = Application([
    (r"/index", IndexHandler,{},'alias_name1'),
    (r"/login", LoginHandler,{},'alias_name2'),
],**settings)

if __name__ == "__main__":
    application.listen(8090)
    ioloop.IOLoop.instance().start()

session_code.py

import json
import time
import hashlib
import settings

def gen_random_str():
    md5 = hashlib.md5()
    md5.update(str(time.time()).encode('utf-8'))
    return md5.hexdigest()


class MemSession(object):
    ''' 静态字段在类里只保存一份(不管实例化多少次,用的都是同一份) '''
    container = {}

    def __init__(self,handler):
        self.handler = handler
        self.session_id = settings.SESSION_ID
        self.expires = settings.EXPIRES
        self.initial()

    def initial(self):
        client_random_str = self.handler.get_cookie(self.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] = {} # 给服务端设置session
        expires = time.time() + self.expires
        self.handler.set_cookie(self.session_id,self.random_str,expires=expires) # 给客户端设置cookie

    def __getitem__(self, item):
        return self.container[self.random_str].get(item)

    def __setitem__(self, key, value):
        self.container[self.random_str][key] = value
        print(self.container)

    def __delitem__(self, key):
        if key in self.container[self.random_str]:
            del self.container[self.random_str][key]

class RedisSession(object):

    def __init__(self,handler):
        self.handler = handler
        self.session_id = settings.SESSION_ID
        self.expires = settings.EXPIRES
        self.initial()

    def get_redis_conn(self):
        import redis
        conn = redis.Redis(host='8.8.8.8', port=6379)
        return conn

    def initial(self):
        self.redis_conn = self.get_redis_conn()
        client_random_str = self.handler.get_cookie(self.session_id)
        if client_random_str and self.redis_conn.exists(client_random_str):
            self.random_str = client_random_str
        else:
            self.random_str = gen_random_str()

        expires = time.time() + self.expires
        self.handler.set_cookie(self.session_id,self.random_str,expires=expires) # 给客户端设置cookie
        self.redis_conn.expire(self.random_str,self.expires) # 给redis设置超时时间

    def __getitem__(self, item):
        # redis 返回的是byte类型,所以需要decode
        data_str = self.redis_conn.hget(self.random_str,item)
        if data_str:
            return json.loads(data_str.decode('utf-8'))

    def __setitem__(self, key, value):
        # val = {'type':type(value).__name__,'value':value}
        self.redis_conn.hset(self.random_str,key,json.dumps(value))

    def __delitem__(self, key):
        self.redis_conn.hdel(self.random_str,key)

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

settings.py

SESSION_ENGINE = "session_code.RedisSession"
SESSION_ID = "__session_id__"
EXPIRES = 300

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1 class="c1">Login</h1>
<form action="" method="post">
    <input type="text" name="name">
    <input type="text" name="pwd">
    <input type="submit" value="提交"> {{ msg }}
</form>


</body>
</html>

  

posted @ 2018-03-11 15:07  lixin[at]hitwh  阅读(250)  评论(0编辑  收藏  举报