• Tornado(二)


    跨站请求伪造CSRF

    开启xsrf(就是叫法不一样和csrf一样),'xsrf_cookies':True 

    settings = {
        'template_path':'template',
        'static_path':'static',
        'static_path_prefix':'/static/',
        'xsrf_cookies':True,
    }

    在post表单中增加csrf认证

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="{{static_url("commons.css")}}" rel="stylesheet" />
    </head>
    <body>
    <h1>index.html</h1>
    <h1>{{ name }}</h1>
    <form action="/index" method="post">
    {% module xsrf_form_html() %} <p>user:<input type="text"/></p> <p>password:<input type="password" /> </p> <input type="submit" value="submit" /> </form> </body> </html>

    网站请求效果(表单中没有增加认证token):

     

    加上token

    AJAX方法

    官方提供:

    获取cookie的token信息  args._xsrf = getCookie("_xsrf");
    function getCookie(name) {
        var r = document.cookie.match("\b" + name + "=([^;]*)\b");
        return r ? r[1] : undefined;
    }
    
    jQuery.postJSON = function(url, args, callback) {
        args._xsrf = getCookie("_xsrf");
        $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
            success: function(response) {
            callback(eval("(" + response + ")"));
        }});
    };

    UI(自定义标签)

    自定义有两种方式:uimethods(方法)和uimodule(模块)

    创建玩一下uimethods,新创建一个py文件

    #_*_coding:utf-8_*_
    
    
    def abcd(self):
        return '自定义uimethod方法'
    uimethods

    主文件

    #_*_coding:utf-8_*_
    import tornado.ioloop
    import tornado.web
    #导入UImethod
    import uimethods #'ui_methods':uimethods 设置下即可 settings = { 'template_path':'template', 'static_path':'static', 'static_path_prefix':'/static/', 'ui_methods':uimethods } class MainHandler(tornado.web.RequestHandler): def get(self): dic={'name':'abc'} self.render('index.html',**dic) app=tornado.web.Application([ (r"/index", MainHandler), ],**settings) if __name__ == "__main__": app.listen(8888) tornado.ioloop.IOLoop.current().start()

    html页面中加入自定方法即可

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="{{static_url("commons.css")}}" rel="stylesheet" />
    </head>
    <body>
    <h1>index.html</h1>
    //自定义方法
    {{abcd()}}
    
    </body>
    </html>

    演示效果

    自定义uimedule(代码改动不多)

    html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="{{static_url("commons.css")}}" rel="stylesheet" />
    </head>
    <body>
    <h1>index.html</h1>
    <p>{{abcd()}}</p>
    <p>自定义module</p>
    //这里可以传递参数
    {% module test_mod(123) %}
    </body>
    </html>
    html

    uimodules文件

    #_*_coding:utf-8_*_
    from tornado.web import UIModule
    
    class test_mod(UIModule):
        #render是死的 最后返回的就是这个方法
        def render(self, *args, **kwargs):
            return 'uimodule.', args
    uimodules

    主文件

    #_*_coding:utf-8_*_
    import tornado.ioloop
    import tornado.web
    import uimethods
    import uimodules
    
    
    settings = {
        'template_path':'template',
        'static_path':'static',
        'static_path_prefix':'/static/',
        'ui_methods':uimethods,
        'ui_modules':uimodules,
    }
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            dic={'name':'abc'}
            self.render('index.html',**dic)
    app=tornado.web.Application([
            (r"/index", MainHandler),
        ],**settings)
    
    if __name__ == "__main__":
        app.listen(8888)
        tornado.ioloop.IOLoop.current().start()
    View Code

    运行效果:

     自定义当方法在以后的应用是很广泛的,tornado 的方法不熟悉,完全可以用这个方法自己写

    用户认证

    简单说一下session和cookie 关系

          由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。
    简图:

    上图可以看出cookie中不会存在敏感信息,重要的信息存储在服务器端的session中

     Tornado是没有session 的,如下图这么处理的

     

    这个方法不怎么安全,连接Tornado机制的就很容易伪造

     实现tornado源生验证(很少会用这个方式来完成登陆验证)

    主文件

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import tornado.ioloop
    import tornado.web
    
    
    class MainHandler(tornado.web.RequestHandler):
    
        def get(self):
            #从cookie中获取key=login_user的值
            login_user = self.get_secure_cookie("login_user", None)
            if login_user:
                #获取cookie成功就打印这个登陆名
                self.write(login_user)
            else:
                self.redirect('/login')
    
    
    class LoginHandler(tornado.web.RequestHandler):
        def get(self):
            #self.current_user()
            self.render('login.html', **{'status': ''})
    
        def post(self, *args, **kwargs):
            #获取前端传递过来的 name和password
            username = self.get_argument('username')
            password = self.get_argument('password')
            if username == 'lily0912' and password == '123':
                #登陆成功就设置cookie,key=login_user,值=lily0912
                self.set_secure_cookie('login_user', 'lily0912')
                #跳转到首页
                self.redirect('/index')
            else:
                self.render('login.html', **{'status': 'name or password error!!!!'})
    
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': '/static/',
        'cookie_secret': 'asdassdasdsd123'
    }
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
        (r"/login", LoginHandler),
    ], **settings)
    
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()

    html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="{{static_url("commons.css")}}" rel="stylesheet" />
    </head>
    <body>
    <h1>登陆</h1>
    <form action="/login" method="post">
        <p>user:<input name="username" type="text"/></p>
        <p>password:<input name="password" type="password" /> </p>
        <input type="submit" value="submit" />
    </form>
    </body>
    </html>
    login

    验证:

     登陆成功后跳转页面,然后可以看到服务器给客户端的cookie信息。

     自定义session验证

    写cookie过程:

    • 将值进行base64加密
    • 对除值意外的内容进行签名,哈希算法(无法逆向解析)
    • 拼接 签名 + 加密值

    读cookie过程:

    • 读取 签名 + 加密值
    • 对签名进行验证
    • base64解密,获取值内容

    所以,我们会将base64加密的值返回给用户。而对于session,他只会将签名返回给用户,然后根据签名获取redis或数据库中保存的其他值。即:在session中,签名和session值的集合是一一对应的。

    python魔法方法(为tornado 增加session功能就靠它了)

    简单说下这个例子是python模仿字典工作

    class Foo(object):
      def __init__(self, key, value):
        self.key = []
        self.value = []
        self.key.append(key)
        self.value.append(value)
    
      def __len__(self):
        return len(self.key)
    
      def __getitem__(self, item):
        try:
          __index = self.key.index(item)
          return self.value[__index]
        except ValueError:
          raise KeyError('can not find the key')
    
      def __setitem__(self, key, value):
        if key not in self.key:
          self.key.append(key)
          self.value.append(value)
        else:
          __index = self.key.index(key)
          self.value[__index] = value
    
      def __delitem__(self, key):
        try:
          __index = self.key.index(key)
          del self.key[__index]
          del self.value[__index]
        except ValueError:
          raise KeyError('can not find the key')
    
      def __str__(self):
        result_list = []
        for index in xrange(len(self.key)):
          __key = self.key[index]
          __value = self.value[index]
          result = __key, __value
          result_list.append(result)
        return str(result_list)
    
      def __iter__(self):
        self.__index = 0
        return self
    
      def next(self):
        if self.__index == len(self.key):
          self.__index = 0
          raise StopIteration()
        else:
          __key = self.key[self.__index]
          __value = self.value[self.__index]
          result = __key, __value
          self.__index += 1
          return result
    
      def __reversed__(self):
        __result = self.value[:]
        __result.reverse()
        return __result
    
      def __contains__(self, item):
        if item in self.value:
          return True
        else:
          return False
    
    
    a = Foo('scolia', 'good')
    a[123] = 321
    a[456] = 654
    a[789] = 987
    print a.key
    print len(a)
    del a[789]
    print a
    for x, y in a:
      print x, y
    print reversed(a)
    print 123 in a
    print 321 in av
    演示代码
    ['scolia', 123, 456, 789]
    4
    [('scolia', 'good'), (123, 321), (456, 654)]
    scolia good
    123 321
    456 654
    [654, 321, 'good']
    False
    True
    执行结果

    这个东西没有弄过的的熟悉一下。确实有的魔法的感觉。

    Tornado 实现session

    显示熟悉一下流程

    application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
    ], **settings)

    根据url匹配都会找到对应的类比如:class LoginHandler(tornado.web.RequestHandler)  

    都会继承这个tornado.web.RequestHandler父类,首先父类会初始化RequestHandler.__init__() ,最后还调用self.initialize(**kwargs)了这个类。这个里面什么都没有。主要是留给我扩展用的 

    最后执行LoginHandler 类中的方法

    我们有做的就是在执行LoginHandler方法之前  生成session 就OK了。

    可以这么做:

    自己写个基类继承tornado.web.RequestHandler 
    class
    BaseHandler(tornado.web.RequestHandler): #继承后直接改写这个方法就行
    def initialize(self):
    在这里写session内容就OK了
    pass 下面的子类直接继承BaseHandler class MainHandler(BaseHandler): class LoginHandler(BaseHandler):

    OK现在开始实现session

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import tornado.ioloop
    import tornado.web
    from hashlib import sha1
    import os, time
    
    #生成session id
    session_id=lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
    
    class Session(object):
        def __init__(self,obj):
            self.obj=obj
        def __getitem__(self, key):
            pass
        def __setitem__(self, key, value):
            #创建session_id
            token=session_id()
            #设置cookie的 key这个无所谓,起个名就行
            cookie_key='_session'
            self.obj.set_secure_cookie(cookie_key,token)
        def __delitem__(self, key):
            pass
    
    
    class BaseHandler(tornado.web.RequestHandler):
        def initialize(self):
            #将self传递给session方法,原因很简单Session方法本身无法将cookie写入LoginHandle对象中。因为它没有这个方法
            self.mysesson=Session(self)
    
    class MainHandler(BaseHandler):
    
        def get(self):
            #从cookie中获取key=login_user的值
            login_user = self.get_secure_cookie("login_user", None)
            if login_user:
                #获取cookie成功就打印这个登陆名
                self.write(login_user)
            else:
                self.redirect('/login')
    
    #RequestHandler.__init__()实例化方法
    class LoginHandler(BaseHandler):
        def get(self):
            #self.current_user()
            self.render('login.html', **{'status': ''})
    
        def post(self, *args, **kwargs):
            username = self.get_argument('username')
            password = self.get_argument('password')
            if username == 'lily0912' and password == '123':
                #self.set_secure_cookie('login_user', 'lily0912')
                #成功登陆就设置状态True
                self.mysesson['login_status']='True'
                self.redirect('/index')
            else:
                self.render('login.html', **{'status': 'name or password error!!!!'})
    
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': '/static/',
        'cookie_secret': 'asdassdasdsd123'
    }
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
        (r"/login", LoginHandler),
    ], **settings)
    
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()

    看下session是否写入成功

  • 相关阅读:
    软件工程第一个个人小程序
    关于程序的单元测试
    求整数数组中子数组最大的和
    电梯调度程序开发 付亚飞 段兴林
    每天听英语系列一
    去除U盘写保护
    显示pdf格式的图片
    在页面判断起始时间是否大于结束时间
    从页面上灵活增删改查
    柱形图,饼状图,折线图JavaScript
  • 原文地址:https://www.cnblogs.com/menkeyi/p/5969153.html
Copyright © 2020-2023  润新知