zoukankan      html  css  js  c++  java
  • web框架详解之 tornado 四 模板引擎、session、验证码、xss

    一、模板引擎

    基本使用
        继承,extends    页面整体布局用继承
        导入,include    如果是小组件等重复的那么就用导入

    下面是目录

     

    首先在controllers里面创建一个文件,文件里面是页面类

    #/usr/bin/env python
    #-*-coding:utf-8 -*-
    import tornado.web
    
    class IndexHandler(tornado.web.RequestHandler):
        def get(self,*args,**kwargs):
            self.render("extend/index.html",List_info=[11,12,13])
    class FFHandler(tornado.web.RequestHandler):
        def get(self, *args, **kwargs):
            self.render("extend/ff.html",List_info=[11,12,13])
    extend代码

    之后在路由系统中配置

    #/bin/usr/env python
    #-*- coding:utf-8 -*-
    import tornado.ioloop
    import tornado.web
    from controllers import home
    from settings import Setting
    from controllers import extend
    
    #路由映射,路由系统
    application=tornado.web.Application(
        [#(r"/index/(?P<page>d*)",home.IndexHandler),
         (r"/index",extend.IndexHandler),
         (r"/ff",extend.FFHandler)
         ],
        **Setting.settings
    )
    # application.add_handlers("www.cnblogs.com",[
    #     (r"/index/(?P<page>d*)",)
    # ])
    
    if __name__=="__main__":
        application.listen(8001)
        tornado.ioloop.IOLoop.instance().start()
    start.py代码

    配置路由系统之后分别创建继承html包和导入HTML包以及母版文件包

    创建extend文件包:

    {% extends "../master/layout.html "%}
    {% block body %}
    <h1>ff</h1>
    
    <h1>end</h1>
    
    <!--这里是导入include目录中form文件中的插件内容,注意这里百分号和括号之间不能有空格
    如果要用这个插件很多分,那么多导入几次就行了
    -->
    {% include '../include/form.html' %}
    {% include '../include/form.html' %}
    {% include '../include/form.html' %}
    {% end %}
    { % block js %}
      <script>
          console.log("ff")
      </script>
    { % end % }
    ff文件代码
    {% extends '../master/layout.html'%}
    
    <!--表示替换掉了layout母版中的css内容-->
    {% block css %}
    <style>
        div{
            border: 1px solid red;
        }
    </style>
    {% end %}
    <!--表示替换了母版中和下面相同的内容-->
    {% block body % }
        <h1>Index</h1>
        {% inclue '../include/form.html'%}
    {% end %}
    
    {% block js %}
    <script>
        console.log("ss")
    </script>
    {% end %}
    index文件代码

    创建木板文件包master

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            .pg-header{
                height: 48px;
                background-color: papayawhip;
            }
            .pg-footer{
                height: 100px;
                background-color: cadetblue;
            }
        </style>
        <!--上面是公用样式,下面是自定义样式-->
        {% block css %}{% end %}
    </head>
    <body>
    <div class="pg-header">
    
    </div>
    <div class="pg-content">
        {% block body %}{% end %}
    </div>
    <div class="pg-footer">adss</div>
    <script src="xxxj"></script>
        {% block js %}{% end %}
    </body>
    </html>
    layout母版文件

    extend就是方便自己定义代码

    include导入文件包

    <form>
        <input type="text"/>
        <input type="submit"/>
    </form>
    
    <ul>
        {% for item in List_info %}
        <li>{{item}}</li>
        {% end %}
    </ul>
    form文件

    这里ff文件导入这个form文件

    在导入文件中也可以和后台进行文件渲染,展示给用户,这里List_info就是在index文件中后台返回给前台进行渲染

    二、cookie

    a)    在浏览器端保存的键值对,特性:每次http请求都会携带
    b)    实现:
    i.    用户验证
    c)    浏览器:
    i.    tornado,在后台设置
    ii.    下面能够用索引取值,是因为tornado在后台进行了分割
    1.    self.cookies  获取所有的cookies
    2.    self.get_cookie(“a”)  获取cookie 中key的值
    3.    self.set_cookie(“key”,”value”)  设置cookie中的值
    iii.    在浏览器上使用jacascript来获取cookie
    1.    document.cookie 表示在当前页面中所有的cookie
    2.    要想获取某一个cookie需要自己设置,因为cookie为字符串,所以要用splite进行分割
    d)    如:
    e)    document.cookie.split(";")
    f)    ["SRCHD=AF=NOFORM", " SRCHUID=V=2&GUID=6FB4DE80646948698D6D4F6B07F7EFED", " SRCHUSR=DOB=20170405", " MUID=1B4DDEB4557967AE1CDAD4EF54D8662D", " HPBingAppQR=CLOSE=1", " WLS=TS=63627045711", " _SS=SID=2DED841E1D526B202FBB8E421CF36A37&HV=1491448911&bIm=31:152", " SRCHHPGUSR=CW=147&CH=937&DPR=1&UTC=480"]
    
    
    3、document.cookie=”k1=xx;path=/;domin(域名):expires”  在浏览器页面设置cookie,并且带上路径
    复习

    JavaScript操作Cookie

     由于Cookie保存在浏览器端,所以在浏览器端也可以使用JavaScript来操作Cookie。

    /*
    toUTCString() 方法可根据世界时 (UTC) 把 Date 对象转换为字符串,并返回结果
    设置cookie,指定秒数过期
     */
    function setCookie(name,value,expires){
        var temp = [];
    var current_date = new Date();        获取当前时间
    // current_date.getSeconds()      获取当前秒
    // current_date.setSeconds        设置秒
    //data.setDate(data.getDate()+7),表示获取超过现在7天的时间
    // current_date                  当前时间+5秒
    // toUTCString()                   当前统一时间
        current_date.setSeconds(current_date.getSeconds() + 5);
    document.cookie = name +"= "+ value +";expires="+ current_date.toUTCString();
    }
    View Code
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>adasd</h1>
        <script>
            /*
    设置cookie,指定秒数过期
     */
    function setCookie(name,value,expires){
        var temp = [];
        var current_date = new Date();
        current_date.setSeconds(current_date.getSeconds() + 5);
        document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
    }
        </script>
    </body>
    </html>
    
    
    然后在浏览器中访问
    setCookie("k22=11",5)            设置cookie,超时时间为5秒
    undefined
    document.cookie                查看cookie
    "k1=999; k22=11= 5"
    document.cookie                5秒后查看
    "k1=999"
    HTML代码

    对于参数

    • domain   指定域名下的cookie
    • path       域名下指定url中的cookie
    • secure    https使用

     jquery中设置cookie

     要用需要下载  这里

    1、    导入jquery
    2、    导入jQuery Cookie Plugin v1.4.1
    注意点:
        如果用jquery导入的时候expires这里如果为数字的时候,表示天数
        如果不想用天数,那么要用,这里的超时时间必须要用toUTCString()统一时间
    
    current_date.setSeconds(current_date.getSeconds() + 5); 用天数,然后用字符串拼接的方式";expires="+ current_date.toUTCString()
    等来设置时间,js数组的.join方法是吧数组变成字符串
                $.cookie(“k1”,”22”,{“path”:””,”domin”:””,expires=1})
                上面的cookie中的数组,在内部用了join方法分割成了字符串
    View Code

    tornado带签名的cookie原理图

    tornado支持两种方式

    • 一、简单的方式
    • 二、签名的方式

    首先服务端让浏览器端生成cookie的时候会经过base64加密,首先生成加密串,
    注意这里的当前时间是
    >>> import time
    >>> time.time()
    1491471613.5271676     --->生成的这个值就是当前时间
    >>>
    加密串 =v1(value)+当前时间+内部自定义字符串
    之后生成的这个cookie就是k1(key)=v1|加密串|当前时间
    
    如何验证这个cookie有没有被篡改:
    客户端向浏览器端发送请求:会把v1和加密串和当前时间发送给浏览器,浏览器内部会经过md5生成一个新的加密串  
    自定义字符串
    +发送过来的时间+v1等于新的加密串,然后加密串进行对比,如果一致就能通过
    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    
    class IndexHandler(tornado.web.RequestHandler):
        #这里判断判断用户登录
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                self.set_cookie("name",self.get_argument("u"))
                # self.set_secure_cookie("name",self.get_argument("u"))
            else:
                self.write("请登录")
    
    
    class ManagerHandler(tornado.web.RequestHandler):
        #如果有cookie的时候就登录
        def get(self):
            if self.get_cookie("name",None) in ["aa","eric"]:
                self.write("欢迎登录:"+self.get_cookie("name"))
            else:
                self.redirect("/index")
    
    settings={
        "template_path":"views",
        "static_path":"statics"
    }
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler)
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    
    上面就是用一种简单的模式登录,登录的时候
    在浏览器中输入
    http://127.0.0.1:8000/index?u=aa
    之后就会执行IndexHandler方法中的get方法首先存入用户输入的cookie,对比后台,然后访问manager网站的时候,判断,如果有对应的cookie那么就会出现欢迎登录
    基于cookie实现用户验证
    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    
    class IndexHandler(tornado.web.RequestHandler):
        #这里判断判断用户登录
        def get(self):
            if self.get_argument("u",None) in ["alex","eric"]:
    这里设置加密的cookie
                self.set_secure_cookie("user",self.get_argument("u"))
            else:
                self.write("请登录")
    
    
    class ManagerHandler(tornado.web.RequestHandler):
        #如果有cookie的时候就登录
        def get(self):
    获取加密的cookie 
            if str(self.get_secure_cookie("user",None),encoding="utf-8") in ["alex","eric"]:
                self.write("欢迎登录:"+str(self.get_secure_cookie("user")))
            else:
                self.redirect("/index")
    
    settings={
        "template_path":"views",
        "static_path":"statics",
    这必须设置配置
        "cookie_secret":"hello",
    }
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler)
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    
    设置加密的cookie用set_secure_cookie()方法,如果获取cookie的时候用get_secure_cookie()
    注意这里获取加密cookie
    注意:这里获取的cookie是byte类型,所以必须要转换一下类型
    下面是带签名的cookie
    Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,
    你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie
    方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。
    你可以把它作为一个关键词参数传入应用的设置中
    签名Cookie的本质是:
    
    写cookie过程:
    
    将值进行base64加密
    对除值以外的内容进行签名,哈希算法(无法逆向解析)
    拼接 签名 + 加密值
    读cookie过程:
    
    读取 签名 + 加密值
    对签名进行验证
    base64解密,获取值内容
    注:许多API验证机制和安全cookie的实现机制相同
    总结

    三、自定义web组件session

     1 如果一直用加密的cookie一直给浏览器那么会导致不安全,以及流量的增大
     2 用一段cookie来代表账号密码,邮箱等等
     3 tornado里面默认没有session,django里面有session
     4 
     5 补充知识:
     6 hashlib.md5.digest() 
     7 hashlib.hexdigest()
     8 是生成MD5的两种表现形式,hashlib.md5.digest() 加密后的结果用二进制表示
     9 二进制由0和1组成,一个字节包含8位二进制码,即包含8位0或1, 1byte可用2个16进制的数来表示
    10 电脑中的数据都是按照16进制来保存的
    11 所以这里要用hexdigest来生成随机数
    12 a[aa]={}:表示a为字典。aa为a的key值,后面的{}表示a中aa为key的value的值
    13     
    14 container内容可以放到1在内存,2在数据库,3在缓存
    View Code

    上图:但用户k1访问服务器的时候会生成aa这个字符串,aa这个字符串保存着用户的各种信息,但用户k2访问服务器的时候,在内部生成bb字符串,用来保存用户的各种信息

    session其实就是定义在服务器端用于保存用户回话的容器,其必须依赖cookie才能实现。

    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    
    #这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
    # 那么http请求断开下次用户登录这个用户信息就会消失
    container={}
    
    class IndexHandler(tornado.web.RequestHandler):
        #这里判断判断用户登录
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                import hashlib
                import time
                #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
                obj=hashlib.md5()
                obj.update(bytes(str(time.time()),encoding="utf-8"))
                random_str=obj.hexdigest()
                container[random_str]={}
                container[random_str]["k1"]=123
                #加上自定义字符串
                container[random_str]["k2"]=self.get_argument("u",None)+"parents"
                #自己定义的,希望以后is_login来确定用户是否上线登录
                container[random_str]["is_login"]=True
    
                #把cookie发送给客户端
                self.set_cookie("iii",random_str)
    
            else:
                self.write("请登录")
    
    
    class ManagerHandler(tornado.web.RequestHandler):
        def get(self):
            random_str=self.get_cookie("iii")
            #获取key对应的值,默认为None
            current_user_info=container.get(random_str,None)
            if not current_user_info:
                self.redirect("/index")
            else:
                if current_user_info.get("is_login",None):
                    temp="%s-%s"%(current_user_info.get("k1",""),current_user_info.get("k2",""))
                    self.write(temp)
                else:
                    self.redirect("/index")
    
    
    settings={
        "template_path":"views",
        "static_path":"statics",
        "cookie_secret":"hello",
    }
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler)
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    python代码
    1、    首先创建session这个容器,让这个容器定义为全局变量
    2、    之后创建随机数,并且让这个随机数定义为字典
    3、    在随机数定义的字典里面,分别定义三类,一类是原本的信息,第二类是让数据加上自定义字符串,第三类标志位
    4、    给客户端发送cookie
    5、    然后用户连接的时候分别判断,随机数,和标志位
    自定义session相当于自己开发httpd三次握手

    session优化封装

     

    上面每一个用户连接都会生成一个随机数,并且随机数(代表字典)里面会保存用户的信息

    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    
    #这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
    # 那么http请求断开下次用户登录这个用户信息就会消失
    container={}
    
            #把Sesson封装起来
    class Session:
        #为了引入IndexHandler的方法,这里的self代表的是s对象
        def __init__(self,handler):
            self.handler=handler
        def __genarate_random_str(self):   #创建随机字符串
            import hashlib
            import time
            #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
            obj=hashlib.md5()
            obj.update(bytes(str(time.time()),encoding="utf-8"))
            random_str=obj.hexdigest()
            return random_str
        def set_value(self,key,value):
            #在container中加入随机字符串,之前要创建随机字符串
            #定义专属于自己的数据
            #在客户端写入随机字符串
            #判断,请求的用户是否已经有随机字符串
            random_str=self.handler.get_cookie("__kakaka__")
            container[random_str]={}
            container[random_str][key]=value
            #这里是为写超时时间做准备
            self.handler.set_cookie("__kakaka__",random_str)
    
    
        def get_value(self,key):  #获取值
            #首先获取客户端的随机字符串
            #从container中获取专属于我的数据
            #专属我的数据[key]
            random_str=self.handler.get_cookie("__kakaka__")
            user_info_dict=container[random_str]
            value=user_info_dict[key]
            return value
    class IndexHandler(tornado.web.RequestHandler):
        #这里判断判断用户登录
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                s=Session(self)
                s.set_value("is_login",True)
            else:
                self.write("请登录")
    
    
    class ManagerHandler(tornado.web.RequestHandler):
        def get(self):
            s=Session(self)
            val=s.get_value("is_login")
            if val:
                self.write("成功")
            else:
                self.write("请重新登录")
    
    settings={
        "template_path":"views",
        "static_path":"statics",
        "cookie_secret":"hello",
    }
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler)
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    python代码
    这段代码是把上面的方法进行了封装,流程:
    1、    用户访问index这个网站,其实就是访问了IndexHandler这个类,会执行get方法,获取用户输入的内容,如果输入的内容符合条件
    2、    初始化Session类中的__init__方法,然后执行set_value方法,并且把传入参数is_login
    3、获取随机数,清空随机数这个字典中的内容,然后把参数传递进去,并且把cookie传递给浏览器
    解析
    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    
    #这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
    # 那么http请求断开下次用户登录这个用户信息就会消失
    container={}
    
            #把Sesson封装起来
    class Session:
        def __init__(self,handler):
            self.handler=handler
            self.random_str=None  #用户连接初始化随机数
        def __genarate_random_str(self):   #创建随机字符串
            import hashlib
            import time
            #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
            obj=hashlib.md5()
            obj.update(bytes(str(time.time()),encoding="utf-8"))
            random_str=obj.hexdigest()
            return random_str
        def set_value(self,key,value):
        #这里判断如果服务端没有随机数
            if not self.random_str:               #用户连接,首先服务端没有随机数,那么去客户端拿随机数
                random_str=self.handler.get_cookie("__kakaka__") #去客户端中拿随机数
                if not  random_str:                           #如果客户端也没有随机数,那么服务端就自己创建随机数
                    random_str=self.__genarate_random_str()  #创建随机数
                    container[random_str]={}                 #清空随机数字典中的内容
                else:
                    if random_str in container.keys():    #如果客户端有随机数,并且为真那么就直接登录成功
                        pass
                    else:                                   #如果客户端到的随机数是伪造的,那么服务端就自己创建随机数
                        random_str=self.__genarate_random_str()
                        container[random_str]={}
                self.random_str=random_str   #最后把上面判断出来的随机数传递给类
            container[self.random_str][key]=value
            #这里是为写超时时间做准备
            self.handler.set_cookie("__kakaka__",self.random_str)  #设置cookie给浏览器,这里可以设置超时时间
    
    
        def get_value(self,key):  #获取值
            random_str=self.handler.get_cookie("__kakaka__")
            if not  random_str:#如果客户端没有随机字符串,就结束
                return None
            user_info_dict=container.get(random_str,None)#客户端有随机字符串,但是内容服务器不匹配,就退出
            if not user_info_dict:
                return None
            value=user_info_dict.get(key,None)   #前面如果都满足,有值就拿值,没有就为None
            return value
    
    
    class IndexHandler(tornado.web.RequestHandler):
        #这里判断判断用户登录
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                s=Session(self)
                s.set_value("is_login",True)
                s.set_value("name",self.get_argument("u",None))
            else:
                self.write("请登录")
    
    
    class ManagerHandler(tornado.web.RequestHandler):
        def get(self):
            s=Session(self)
            val=s.get_value("is_login")
            if val:
                self.write(s.get_value("name"))
            else:
                self.write("请重新登录")
    
    
    settings={
        "template_path":"views",
        "static_path":"statics",
        "cookie_secret":"hello",
    }
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler)
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    session优化:
    用户如果直接连接manager会提示必须登录,主要原因是浏览器cookie中没有登录信息
    1、    用户访问index网页的时候就是访问IndexHandler这个类,用户连接,服务器内部就会初始化随机数
    2、    服务器就会执行set_value方法,并且传入参数is_login参数,首先为了判断用户是否第一次登陆,所以用if not self.random_str,没有就用get_cookie()方法去客户端中拿随机数,这里需要判断,如果客户端也没有随机数,那么服务端就要自己创建随机数,并且把这个随机数传递给服务器这个类;如果客户端有随机数,要判断这个随机数是否是伪造的,如果是伪造的,服务器需要自己创建随机数,并且把这个随机数传递给服务器这个类;之后把is_login参数替代key传递给session这个字典求出来value这个值,并且设置一下这个cookie传递给浏览器;然后设置key为name的cookie
    3、    用户访问manager这个网站,会执行get方法,并且获取浏览器随机数,如果浏览器中没有随机数或者浏览器的随机数是伪造的,那么就会退出,如果经过了2这个步骤,那么就能登录成功并且得到设置cookie中key为name的值
    流程详解:

    优化:

    在Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作

    这里的initialize就是钩子函数

     优化一

    #定义tornado中的钩子函数和反射函数来优化下面的类
    class BaseHandler(tornado.web.RequestHandler):
        def initialize(self):
            self.session=Session(self)
    
    class IndexHandler(BaseHandler):
        #这里判断判断用户登录,get方法是被反射调用的getattr
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                self.session.set_value("is_login",True)
                self.session.set_value("name",self.get_argument("u",None))
            else:
                self.write("请登录")
    
    class ManagerHandler(BaseHandler):
        def get(self):
            val=self.session.get_value("is_login")
            if val:
                self.write(self.session.get_value("name"))
            else:
                self.write("请重新登录")
    View Code

    让这两个类继承一个共同的父类,利用tornado内置的钩子函数来优化代码

    优化二、利用__getitem__   __setitem__方法

    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    
    #这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
    # 那么http请求断开下次用户登录这个用户信息就会消失
    container={}
    
            #把Sesson封装起来
    class Session:
        def __init__(self,handler):
            self.handler=handler
            self.random_str=None  #用户连接初始化随机数
        def __genarate_random_str(self):   #创建随机字符串
            import hashlib
            import time
            #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
            obj=hashlib.md5()
            obj.update(bytes(str(time.time()),encoding="utf-8"))
            random_str=obj.hexdigest()
            return random_str
        def __setitem__(self,key,value):
            #这里判断如果服务端没有随机数
            if not self.random_str:               #用户连接,首先服务端没有随机数,那么去客户端拿随机数
                random_str=self.handler.get_cookie("__kakaka__") #去客户端中拿随机数
                if not  random_str:                           #如果客户端也没有随机数,那么服务端就自己创建随机数
                    random_str=self.__genarate_random_str()  #创建随机数
                    container[random_str]={}                 #清空随机数字典中的内容
                else:
                    if random_str in container.keys():    #如果客户端有随机数,并且为真那么就直接登录成功
                        pass
                    else:                                   #如果客户端到的随机数是伪造的,那么服务端就自己创建随机数
                        random_str=self.__genarate_random_str()
                        container[random_str]={}
                self.random_str=random_str   #最后把上面判断出来的随机数传递给类
            container[self.random_str][key]=value
            #这里是为写超时时间做准备
            self.handler.set_cookie("__kakaka__",self.random_str)  #设置cookie给浏览器,这里可以设置超时时间
    
    
        def __getitem__(self,key):  #获取值
            random_str=self.handler.get_cookie("__kakaka__")
            if not  random_str:#如果客户端没有随机字符串,就结束
                return None
            user_info_dict=container.get(random_str,None)#客户端有随机字符串,但是内容服务器不匹配,就退出
            if not user_info_dict:
                return None
            value=user_info_dict.get(key,None)   #前面如果都满足,有值就拿值,没有就为None
            return value
    
    #定义tornado中的钩子函数和反射函数来优化下面的类
    class BaseHandler(tornado.web.RequestHandler):
        def initialize(self):
            self.session=Session(self)
    
    class IndexHandler(BaseHandler):
        #这里判断判断用户登录,get方法是被反射调用的getattr
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                self.session["is_login"]=True
                self.session["name"]=self.get_argument("u",None)
                # self.session.set_value("is_login",True)
                # self.session.set_value("name",self.get_argument("u",None))
            else:
                self.write("请登录")
    
    class ManagerHandler(BaseHandler):
        def get(self):
            val=self.session["is_login"]
            if val:
                self.write(self.session["name"])
            else:
                self.write("请重新登录")
    
    
    settings={
        "template_path":"views",
        "static_path":"statics",
        "cookie_secret":"hello",
    }
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler)
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    View Code

    所有的web框架都是session[key]=value的方法实现的

    这里只需要改一下名字就可以

    1、
    placeholder 属性提供可描述输入字段预期值的提示信息(hint)。
    该提示会在输入字段为空时显示,并会在字段获得焦点时消失。
    2、
    open打开一个文件的时候里面要加上r表示不用转义了
    open(r”路径”)
    补充知识:

    四、验证码

    验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。
    这个验证码是放在tornado的session里面的
    验证码机制:服务器首先创建验证码,并且把验证码放入到随机数这个字典里面,用户通过get方法接收到验证码,然后用户输入验证码和账户信息发送给服务器,服务器通过对比用户发来的验证码和自己产生的验证码,(这里要创建不分辨大小写,可以让用户输入的和自己产生的转成全部大写或者全部小写)对比,如果一样那么就显示登录成功,如果没有一样,那么就显示输入的验证码错误。并且在前端添加一个点击事件,只要用户一点击那么验证码就会刷新
    说明

    安装图像处理模块:

    1

    pip3 install pillow

     下载下面源码之后,需要把check_code.py和Monaco.ttf导入到这个代码目录中(仅仅限制与python3.5)

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            .aa{
                cursor: pointer;
            }
        </style>
    </head>
    <body>
        <h1>请输入登录信息</h1>
        <form action="/login" method="post">
            <p><input name="user" type="text" placeholder="用户名" /></p>
            <p><input name="pwd" type="password" placeholder="密码" /></p>
            <p>
                <input name='code' type="text" placeholder="验证码" />
                <img class="aa" src="/check_code" onclick='ChangeCode();' id='imgCode'>
            </p>
            <input type="submit" value="提交"/><span style="color: red">{{status}}</span>
        </form>
       <script type="text/javascript">
    
           function ChangeCode() {
                var code = document.getElementById('imgCode');
                //url后面只能添加问号,添加问号就是改变地址
                code.src += '?';
            }
        </script>
    </body>
    </html>
    login.html
    #/usr/bin/env python
    #-*- coding:utf-8-*-
    import tornado.ioloop
    import tornado.web
    import tornado.httpserver
    import tornado.ioloop
    import tornado.process
    import tornado.web
    #
    
    
    #这个字典必须定制成为全局变量用来保存用户的信息,如果是局部变量,
    # 那么http请求断开下次用户登录这个用户信息就会消失
    container={}
    
            #把Sesson封装起来
    class Session:
        def __init__(self,handler):
            self.handler=handler
            self.random_str=None  #用户连接初始化随机数
        def __genarate_random_str(self):   #创建随机字符串
            import hashlib
            import time
            #首先通过md5生成随机数据,电脑中的数据都是16进制保存的
            obj=hashlib.md5()
            obj.update(bytes(str(time.time()),encoding="utf-8"))
            random_str=obj.hexdigest()
            return random_str
        def __setitem__(self,key,value):
            #这里判断如果服务端没有随机数
            if not self.random_str:               #用户连接,首先服务端没有随机数,那么去客户端拿随机数
                random_str=self.handler.get_cookie("__kakaka__") #去客户端中拿随机数
                if not  random_str:                           #如果客户端也没有随机数,那么服务端就自己创建随机数
                    random_str=self.__genarate_random_str()  #创建随机数
                    container[random_str]={}                 #清空随机数字典中的内容
                else:
                    if random_str in container.keys():    #如果客户端有随机数,并且为真那么就直接登录成功
                        pass
                    else:                                   #如果客户端到的随机数是伪造的,那么服务端就自己创建随机数
                        random_str=self.__genarate_random_str()
                        container[random_str]={}
                self.random_str=random_str   #最后把上面判断出来的随机数传递给类
            container[self.random_str][key]=value
            #这里是为写超时时间做准备
            self.handler.set_cookie("__kakaka__",self.random_str)  #设置cookie给浏览器,这里可以设置超时时间
    
    
        def __getitem__(self,key):  #获取值
            random_str=self.handler.get_cookie("__kakaka__")
            if not  random_str:#如果客户端没有随机字符串,就结束
                return None
            user_info_dict=container.get(random_str,None)#客户端有随机字符串,但是内容服务器不匹配,就退出
            if not user_info_dict:
                return None
            value=user_info_dict.get(key,None)   #前面如果都满足,有值就拿值,没有就为None
            return value
    
    #定义tornado中的钩子函数和反射函数来优化下面的类
    class BaseHandler(tornado.web.RequestHandler):
        def initialize(self):
            self.session=Session(self)
    
    class IndexHandler(BaseHandler):
        #这里判断判断用户登录,get方法是被反射调用的getattr
        def get(self):
            if self.get_argument("u",None) in ["aa","eric"]:
                self.session["is_login"]=True
                self.session["name"]=self.get_argument("u",None)
                # self.session.set_value("is_login",True)
                # self.session.set_value("name",self.get_argument("u",None))
            else:
                self.write("请登录")
    
    class ManagerHandler(BaseHandler):
        def get(self):
            val=self.session["is_login"]
            if val:
                self.write(self.session["name"])
            else:
                self.write("请重新登录")
    # class CheckCodeHandler(BaseHandler):
    #     def get(self):
    #         import io
    #         import check_code
    #
    #         mstream = io.BytesIO()
    #         img, code = check_code.create_validate_code()
    #         img.save(mstream, "GIF")
    #         # self.session["CheckCode"] = code
    #         self.write(mstream.getvalue())
    class MainHandler(BaseHandler):
        def get(self):
            self.render('login.html',status="")
        def post(self, *args, **kwargs):
            user=self.get_argument("user",None)
            pwd=self.get_argument("pwd",None)
            code=self.get_argument("code",None)
            #比较用户输入的验证码和服务器给出的验证码的值
            check_code=self.session["CheckCode"]
            if code.upper()==check_code.upper():
                self.write("验证码正确")
            else:
                # self.redirect("/login")
                self.render("login.html",status="验证码错误")
    
    
    class CheckCodeHandler(BaseHandler):
        def get(self, *args, **kwargs):
            #生成图片并且返回
            import io
            import check_code
            #建立内存级别文件,相当于一个容器
            mstream = io.BytesIO()
            #创建图片并且写入验证码
            img, code = check_code.create_validate_code()
            #将图片内容写入到IO中mstream
            img.save(mstream, "GIF")
            #为每个用户保存其对应的验证码
            self.session["CheckCode"] = code
            self.write(mstream.getvalue())
    
    settings={
        'template_path': 'views',
        'static_path': 'static',
        "static_url_prefix":"/statics/",
        "cookie_secret":"hello",
        # "xsrf_cookies":True,
    }
    
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler),
        # (r"/login",LoginHandler),
        (r"/login",MainHandler),
        (r"/check_code",CheckCodeHandler),
    
    
    ],**settings)
    
    if __name__=="__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    python代码

    验证码Demo源码下载:猛击这里

     

    五、CSRF

    会集群要会:分布式哈希haxi redis
    CSRF限制post请求的
    用户访问是先请求服务器调用get请求,然后发送post请求,之后服务器会给用户一个随机字符串,当用户离开后,下次再访问会带着这个随机字符串访问服务器,如果用户没有这个随机字符串,那么CSRF会阻止这个用户请求,这样可以使服务器免遭受恶意攻击造成服务器宕机
    View Code

    要加上CSRF:

    1、在配置文件中加上配置文件”xsrf_cookies”:True

    2、在前台代码中加上{% raw xsrf__form_html %}

    class CsrfHandler(BaseHandler):
        def get(self, *args, **kwargs):
            self.render("csrf.html")
        def post(self, *args, **kwargs):
            self.write("csrf.post")
    settings={
        'template_path': 'views',
        'static_path': 'static',
        "static_url_prefix":"/statics/",
        "cookie_secret":"hello",
        "xsrf_cookies":True,   这里加上配置文件
    }
    
    application=tornado.web.Application([
        (r"/index",IndexHandler),
        (r"/manager",ManagerHandler),
        # (r"/login",LoginHandler),
        (r"/login",MainHandler),
        (r"/check_code",CheckCodeHandler),
        (r"/csrf",CsrfHandler)
    
    
    ],**settings)
    View Code

    html代码上面加上

    <form action="/csrf" method="post">
        {% raw xsrf_form_html() %}
        <p><input type="text" placeholder="用户"/></p>
        <p><input type="text" placeholder="密码"/></p>
        <p>
            <input name="code" type="text" placeholder="验证码"/>
            <!--<img src="/check_code">-->
        </p>
        <input type="submit" value="Submit"/>
    View Code

    提交的是AJAX的post请求

    如果你提交的是 AJAX 的 POST 请求,你还是需要在每一个请求中通过脚本添加上 _xsrf 这个值。下面是在 FriendFeed 中的 AJAX 的 POST 请求,使用了 jQuery 函数来为所有请求组东添加 _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 + ")"));
        }});
    };
    

    对于 PUT 和 DELETE 请求(以及不使用将 form 内容作为参数的 POST 请求) 来说,你也可以在 HTTP 头中以 X-XSRFToken这个参数传递 XSRF token。

    如果你需要针对每一个请求处理器定制 XSRF 行为,你可以重写 RequestHandler.check_xsrf_cookie()。例如你需要使用一个不支持 cookie 的 API, 你可以通过将 check_xsrf_cookie() 函数设空来禁用 XSRF 保护机制。然而如果 你需要同时支持 cookie 和非 cookie 认证方式,那么只要当前请求是通过 cookie 进行认证的,你就应该对其使用 XSRF 保护机制,这一点至关重要。

     六、总结

    1、cookie和session的区别
    1)cookie是保存在客户端的,session是保存在服务端的,因为服务器端,表示可能在内存中,可能在数据库端,可能在缓存中统称为服务器端
    2、session和cookie有什么联系?:
    答:有。session是通过cookie人为构建起来的,在web开发里面本身没有session这个东西的。在服务器端可以高层一个数据库,可以在内存中搞成一个字典,每一次用户来访问的时候,给用户发一对token,下一次,用户访问再带着这一对token来,服务器端就知道你是不是上一次的你。如果再问就来画一张图
    
    3、分页 XSS  跨站脚本攻击
    4、csrf 工作方式:
    答:跨站请求伪造
    验证:第一次请求的时候是get方式请求,防止没有经过验证就来post请求,造成大并发机器宕机
    5、    Ajax
    为什么要有Ajax
    答:防止页面批量刷新
        利用:
            iframe   忽略
            XMLHttpRequest
                自己写
                xhr
                xhr.open()
                xhr.onreadystatechange
                xhr.send()
            jQuery
            会用下面的就会jquery,ajax
                $.ajax({
                url:
                type
                data
                dataType
                success
                error
    })
    6、    验证码、
    7、    上传文件
    form标签
            form标签 enctype=““form标签里面必须要有这个才能进行上传文件
    通过Ajax上传文件
            利用formDate()
                XMLHttpRequest
                jQuery
            iframe+form标签为了兼容性设计,ifram就相当于设置一个通道,form把数据提交到这个通道,然后不刷页面上传文件
    重点总结
  • 相关阅读:
    IE6下实现Width:auto
    Dynamic Linq 的Like扩展
    用一句JQuery代码实现表格的简单筛选
    jquery:利用jsonp跨域访问转载
    LINQ动态组合查询
    Windows服务中Timer组件
    正则表达式笔记转载
    继LINQ动态组合查询PredicateExtensions讲解
    错误笔记:在OleDb执行下Access ,程序不报错,但是Update也更新不成功的
    jQuery 1.4 版本的十五个新特性转载
  • 原文地址:https://www.cnblogs.com/pyrene/p/6709582.html
Copyright © 2011-2022 走看看