一、模板引擎
基本使用
繼承,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])
之后在路由系統中配置

#/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()
配置路由系統之后分別創建繼承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 % }

{% 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 %}
創建木板文件包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>
extend就是方便自己定義代碼
include導入文件包

<form> <input type="text"/> <input type="submit"/> </form> <ul> {% for item in List_info %} <li>{{item}}</li> {% end %} </ul>
這里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(); }

<!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"
對於參數
- 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方法分割成了字符串
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那么就會出現歡迎登錄

#/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 中保存當前登陸用戶的 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在緩存
上圖:但用戶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()
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()

這段代碼是把上面的方法進行了封裝,流程: 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()

用戶如果直接連接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("請重新登錄")
讓這兩個類繼承一個共同的父類,利用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()
所有的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>

#/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()
驗證碼Demo源碼下載:猛擊這里
五、CSRF

會集群要會:分布式哈希haxi redis
CSRF限制post請求的
用戶訪問是先請求服務器調用get請求,然后發送post請求,之后服務器會給用戶一個隨機字符串,當用戶離開后,下次再訪問會帶着這個隨機字符串訪問服務器,如果用戶沒有這個隨機字符串,那么CSRF會阻止這個用戶請求,這樣可以使服務器免遭受惡意攻擊造成服務器宕機
要加上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)
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"/>
提交的是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把數據提交到這個通道,然后不刷頁面上傳文件