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把數據提交到這個通道,然后不刷頁面上傳文件
重點總結

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM