Tornado-Secure cookie and Session

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tornado-Secure cookie and Session相关的知识,希望对你有一定的参考价值。

这一节涉及的内容有:

1.客户端和服务端操作cookie的方法

2.secure cookie的机制

3.基本/自定义session

文件结构

技术分享图片

三个启动文件由下往上对应的分别是三种服务端:使用secure cookie,使用基本的session,使用自定义session。

另外,这一节中的index.html中并任何实质的内容,我只是在里面练习写了一个服务端生成自定义时间的键值对cookie,同样附在下面

Python代码

start.py

技术分享图片
from tornado import web
import tornado.ioloop


class IndexHandler(web.RequestHandler):
    def get(self):
        if self.get_argument(user, None) in [yeff, mike]:
            self.set_secure_cookie(n, self.get_argument(user))
            self.write(欢迎)
        else:
            self.write(请登陆)


class ManagerHandler(web.RequestHandler):
    def get(self):
        # 注意这里取得的cookie是bytes格式的,不是字符串格式
        if self.get_secure_cookie(n, None) in [byeff, bmike]:
            self.write(欢迎登陆:  + str(self.get_secure_cookie(n),encoding="utf-8"))
        else:
            self.redirect(/index)


settings = {
    "template_path": "views",
    "static_path": "static",
    "cookie_secret": "salt",
}

application = web.Application([
    (r"/index", IndexHandler),
    (r"/manager", ManagerHandler),
], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
使用secure cookie的服务端

 

session-start.py

还有很大的缺陷,如登陆后再装到index还是会提示未登陆。这一点在自定义session中有修正。

技术分享图片
from tornado import web
import tornado.ioloop
import time
import hashlib

sessions = {}

# 可以注意到在用了session后,每次重启客户端后,用户拿着cookie也是没有办法登陆的
# 因为客户端这边的sessions中的内容被清空了,好比用户手里虽然还有钥匙(cookie键值对)
# 但服务端的箱子已经没有了
# 当然sessions可以放在数据库/文件/缓存中,而不是存储在内存中
# cookie是否使用完全看服务端的需求:只要将对应user的islogin信息换成False即可
# 通过在服务端和cookie间多添加一层抽象,既便于存储大量信息,也提升了安全性


class IndexHandler(web.RequestHandler):
    def get(self):
        if self.get_argument(usn, None) in [yeff, mike]:
            _en = hashlib.md5()
            _en.update(bytes(str(time.time()), encoding="utf-8"))
            user_key = _en.hexdigest()
            sessions[user_key] = {}
            sessions[user_key][name] = Yifei Xu
            sessions[user_key][age] = 23
            sessions[user_key][accountInfo] = xyfst
            sessions[user_key][isLogin] = True
            self.set_cookie(name=steam, value=user_key)
            self.write(登陆成功了哦@[email protected])
        else:
            self.write(请登陆-_-)


class ManagerHandler(web.RequestHandler):
    def get(self):
        user_key = self.get_cookie(name=steam)
        user_info = sessions.get(user_key)
        if not user_info:
            self.redirect("/index")
        else:
            if user_info.get(isLogin):
                display_str = "Name:%s\\tAge:%s\\tAccount:%s" % (user_info.get(name), user_info.get(age), user_info.get(accountInfo))
                self.write(display_str)
            else:
                self.write("登陆信息已失效,得重新登陆啦*_*")


settings = {
    "template_path": "views",
    "static_path": "static",
}

application = web.Application([
    (r"/index", IndexHandler),
    (r"/manager", ManagerHandler),
], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
使用基本session的服务端

 class-session-start.py

技术分享图片
from tornado import web
import tornado.ioloop

SESSIONS = {}


class Sessions(object):
    def __init__(self, handler, cookie_key):
        self.handler = handler
        self.user_key = None
        self.cookie_key = cookie_key

    @staticmethod
    def generate_random_str():
        import hashlib, time
        _en = hashlib.md5()
        _en.update(bytes(str(time.time()), encoding="utf-8"))
        return _en.hexdigest()

    def __setitem__(self, k, v):
        # 创建随机字符串
        # 创建自己的箱子
        # 在箱子中放入信息对
        # 在客户端中放入箱子的钥匙

        # 如果服务端还没有钥匙,先制作一个钥匙,并创建一个对应的箱子
        if not SESSIONS.get(self.user_key):
            self.user_key = Sessions.generate_random_str()
            SESSIONS[self.user_key] = {}
        # 起初/manager页面无论如何都无法进入,想了很久
        # 这个点费了我不少时间才找到:这边对SESSIONS中是否存在钥匙没有判断,使得每次set方法清空钥匙对应的箱子
        SESSIONS[self.user_key][k] = v
        self.handler.set_cookie(self.cookie_key, self.user_key)

    def __getitem__(self, item):
        # 获取客户端递来的钥匙
        # 看看房间里有对应钥匙吗
        # 用钥匙打开箱子,获得信息对,取出值
        # 没有钥匙返回None
        value = None
        _user_key = self.handler.get_cookie(self.cookie_key)
        if SESSIONS.get(_user_key):
            self.user_key = _user_key
            value = SESSIONS[self.user_key][item]
        return value


# IndexHandler和ManagerHandler都继承这个类
# tornado为我们留的钩子,使用继承RequestHandler的类并初始化时(执行__init__方法),会在最后执行 self.initialize(**kwargs)
# 我们可以自己定义initialize的函数内容,以实现不同的效果
# 这里我们是初始化了服务端的Sessions类,继承之后,两个类就不用自己再初始化了,更简洁一些
class BaseHandler(web.RequestHandler):
    def initialize(self):
        self.session = Sessions(self, steam)


# tornado内部通过反射调用get和post方法
# obj = IndexxHandler()
# func = getattr(obj, "get")
# func()
class IndexHandler(BaseHandler):
    def get(self):
        # 如果客户端有对应的钥匙则转到manager页面尝试匹配
        if SESSIONS.get(self.get_cookie(steam)):
            self.redirect(/manager)
        # 注意这里的多层嵌套
        # 第一次我将两个if写在了同级,但在执行了redirect函数后,还是会接着执行下面判断中的else块
        # 虽不至于中止程序,但会报错,所以这里写成了两层嵌套
        else:
            # 判断用户登陆信息是否正确(这里作了简化,只判断了用户名)
            if self.get_argument(usn, None) in [yeff, mike]:
                # session = Sessions(self, ‘steam‘)
                self.session[isLogin] = True
                self.session[name] = Yifei Xu
                self.session[age] = 23
                self.session[accountInfo] = xyfst
                self.write(登陆成功了哦@[email protected])
            else:
                self.write(请先登陆呦)


class ManagerHandler(BaseHandler):
    def get(self):
        # session = Sessions(self, ‘steam‘)

        # 从客户端拿来钥匙
        # 看看服务端的房间里有钥匙吗
        # 有则取出对应的信息对
        _site_user_key = self.get_cookie(self.session.cookie_key)
        if not SESSIONS.get(_site_user_key):
            self.write(登陆信息已失效,请先登陆*。*)
        else:
            display_str = "Name:%s\\tAge:%s\\tAccount:%s" % (self.session[name], self.session[age], self.session[accountInfo])
            self.write(display_str)


settings = {
    "template_path": "views",
    "static_path": "static",
}

application = web.Application([
    (r"/index", IndexHandler),
    (r"/manager", ManagerHandler),
], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
使用自定义session的服务端

 HTML代码

index.html

并没有实质的实际内容,内部只有一个我自己写的生成自定义cookie的方法

 

技术分享图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Cookie</h1>
<script>
    <!--根据用户输入设置cookie,时间单位可以是yMdhms,格式是数字+对应字母-->
    <!--也可以针对不同单位的时间分开写方法,我这里整合为了一种方法,顺便复习了js的字符串操作方法以及switch语法-->
    function setCookie(name,value,expires){
        var current_date = new Date();
        if(typeof(expires) == string){
            var unit = expires.charAt(expires.length-1);
            var str_num = expires.slice(0,expires.length-1);
            var num = parseInt(str_num);
            if(yMdhms.includes(unit) && num){
                switch(unit){
                    case y:
                        current_date.setDate(current_date.getDate() + num * 365);
                    case M:
                        current_date.setDate(current_date.getDate() + num * 30);
                    case d:
                        current_date.setDate(current_date.getDate() + num);
                    case h:
                        current_date.setSeconds(current_date.getSeconds() + num * 60 * 60);
                    case m:
                        current_date.setSeconds(current_date.getSeconds() + num * 60);
                    case s:
                        current_date.setSeconds(current_date.getSeconds() + num);
                }
            }else{
                return false;
            }
        }else{
            return false;
        }
        document.cookie = name + "=" + value + ";expires=" + current_date.toUTCString();
        return true;
    }

</script>
</body>
</html>
浏览器端生成自定义cokie

 

额外内容

这一节也涉及到了python类的特殊方法以及钩子函数,在注释里都有讲到,下面也附带一些解释。

class Foo:
    def __call__(self, *args, **kwargs):
        pass
    
    def __init__(self):
        pass
    
    def __class__(self):
        pass
    
    def __setitem__(self, key, value):
        pass
    
    def __getitem__(self, item):
        pass
    
    def __delitem__(self, key):
        pass
    
    
obj = Foo()       # __call__, __init__
obj[k1] = v1  # __setitem__
obj[k1]         # __getitem__
del obj[k1]     # __delitem__    

 

钩子函数: http://blog.csdn.net/sunstars2009918/article/details/39340449(这位作者讲的很清晰,虽然例子是C。*-*)

 

以上是关于Tornado-Secure cookie and Session的主要内容,如果未能解决你的问题,请参考以下文章

Cookie and Session

sessions and cookies

Introduction of Cookie and Session

Cookie and Session

cookies and token知识点

cookie and session