Tornado就是咱們在 FriendFeed 的 Web 服務器及其經常使用工具的開源版本。Tornado 和如今的主流 Web 服務器框架(包括大多數 Python 的框架)有着明顯的區別:它是非阻塞式服務器,並且速度至關快。得利於其 非阻塞的方式和對epoll的運用,Tornado 每秒能夠處理數以千計的鏈接,所以 Tornado 是實時 Web 服務的一個 理想框架。咱們開發這個 Web 服務器的主要目的就是爲了處理 FriendFeed 的實時功能 ——在 FriendFeed 的應用裏每個活動用戶都會保持着一個服務器鏈接。(關於如何擴容 服務器,以處理數以千計的客戶端的鏈接的問題,請參閱The C10K problem)
javascript
Tornado表明嵌入實時應用中最新一代的開發和執行環境。 Tornado 包含三個完整的部分:css
(1)、Tornado系列工具, 一套位於主機或目標機上強大的交互式開發工具和使用程序;html
(2)、VxWorks 系統, 目標板上高性能可擴展的實時操做系統;前端
(3)、可選用的鏈接主機和目標機的通信軟件包 如以太網、串行線、在線仿真器或ROM仿真器java
Tornado的獨特之處在於其全部開發工具可以使用在應用開發的任意階段以及任何檔次的硬件資源上。並且,完整集的Tornado工具可使開發人員徹底不用考慮與目標鏈接的策略或目標存儲區大小。Tornado 結構的專門設計爲開發人員和第三方工具廠商提供了一個開放環境。已有部分應用程序接口能夠利用並附帶參考書目,內容從開發環境接口到鏈接實現。Tornado包括強大的開發和調試工具,尤爲適用於面對大量問題的嵌入式開發人員。這些工具包括C和C++源碼級別的調試器,目標和工具管理,系統目標跟蹤,內存使用分析和自動配置. 另外,全部工具能很方便地同時運行,很容易增長和交互式開發。python
最重要的一個模塊是web
, 它就是包含了 Tornado 的大部分主要功能的 Web 框架。其它的模塊都是工具性質的, 以便讓 web
模塊更加有用 後面的 Tornado 攻略 詳細講解了 web
模塊的使用方法。jquery
主要模塊ios
web
- FriendFeed 使用的基礎 Web 框架,包含了 Tornado 的大多數重要的功能escape
- XHTML, JSON, URL 的編碼/解碼方法database
- 對 MySQLdb
的簡單封裝,使其更容易使用template
- 基於 Python 的 web 模板系統httpclient
- 非阻塞式 HTTP 客戶端,它被設計用來和 web
及 httpserver
協同工做auth
- 第三方認證的實現(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)locale
- 針對本地化和翻譯的支持options
- 命令行和配置文件解析工具,針對服務器環境作了優化httpserver
- 服務於 web
模塊的一個很是簡單的 HTTP 服務器的實現iostream
- 對非阻塞式的 socket 的簡單封裝,以方便經常使用讀寫操做ioloop
- 核心的 I/O 循環pip install tornado
源碼安裝:https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.web import tornado.ioloop class IndexHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.write("Hello World, My name is 賭神") application = tornado.web.Application([ (r'/index',IndexHandler), ]) if __name__ == "__main__": application.listen(8080) tornado.ioloop.IOLoop.instance().start()
第一步:執行腳本,監聽 8080 端口git
第二步:瀏覽器客戶端訪問 /index --> http://127.0.0.1:8080/indexgithub
第三步:服務器接受請求,並交由對應的類處理該請求
第四步:類接受到請求以後,根據請求方式(post / get / delete ...)的不一樣調用並執行相應的方法
第五步:而後將類的方法返回給瀏覽器
在tornado web框架中,路由表中的任意一項是一個元組,每一個元組包含pattern(模式)和handler(處理器)。當httpserver接收到一個http請求,server從接收到的請求中解析出url path(http協議start line中),而後順序遍歷路由表,若是發現url path能夠匹配某個pattern,則將此http request交給web應用中對應的handler去處理。
因爲有了url路由機制,web應用開發者沒必要和複雜的http server層代碼打交道,只須要寫好web應用層的邏輯(handler)便可。Tornado中每一個url對應的是一個類
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 __auth__ = "dushen"
4
5 import tornado.web 6 import tornado.ioloop 7
8 class IndexHandler(tornado.web.RequestHandler): 9
10 def get(self, *args, **kwargs): 11 self.write("Hello World, My name is 賭神") 12
13 class LoginHandler(tornado.web.RequestHandler): 14
15 def get(self, *args, **kwargs): 16 self.write("<input type = 'text'>") 17
18 class RegisterHandler(tornado.web.RequestHandler): 19
20 def get(self, *args, **kwargs): 21 self.write("<input type = 'password'>") 22
23 application = tornado.web.Application([ 24 (r'/index/(?P<page>\d*)',IndexHandler), # 基礎正則路由
25 (r'/login',LoginHandler), 26 (r'/register',RegisterHandler), 27 ])
觀察全部的網頁的內容,下面都有分頁,當點擊下一頁後面的數字也就跟着變了,這種就能夠用基礎正則路由來作,下面我來給你們下一個網頁分頁的案例吧
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 __auth__ = "zhangyanlin" 4 5 import tornado.web 6 import tornado.ioloop 7 8 LIST_INFO = [ 9 {'username':'zhangyanlin','email':'133@164.com'} 10 ] 11 for i in range(200): 12 temp = {'username':str(i)+"zhang",'email':str(i)+"@163.com"} 13 LIST_INFO.append(temp) 14 15 16 class Pagenation: 17 18 def __init__(self,current_page,all_item,base_url): #當前頁 內容總數 目錄 19 try: 20 page = int(current_page) 21 except: 22 page = 1 23 if page < 1: 24 page = 1 25 26 all_page,c = divmod(all_item,5) 27 if c > 0: 28 all_page +=1 29 30 self.current_page = page 31 self.all_page = all_page 32 self.base_url = base_url 33 34 @property 35 def start(self): 36 return (self.current_page - 1) * 5 37 38 @property 39 def end(self): 40 return self.current_page * 5 41 42 def string_pager(self): 43 list_page = [] 44 if self.all_page < 11: 45 s = 1 46 t = self.all_page + 1 47 else: 48 if self.current_page < 6: 49 s = 1 50 t = 12 51 else: 52 if (self.current_page + 5) < self.all_page: 53 s = self.current_page-5 54 t = self.current_page + 6 55 else: 56 s = self.all_page - 11 57 t = self.all_page +1 58 59 first = '<a href = "/index/1">首頁</a>' 60 list_page.append(first) 61 # 當前頁 62 if self.current_page == 1: 63 prev = '<a href = "javascript:void(0):">上一頁</a>' 64 else: 65 prev = '<a href = "/index/%s">上一頁</a>'%(self.current_page-1,) 66 list_page.append(prev) 67 68 #頁碼 69 for p in range(s,t): 70 if p== self.current_page: 71 temp = '<a class = "active" href = "/index/%s">%s</a>'%(p,p) 72 else: 73 temp = '<a href = "/index/%s">%s</a>' % (p, p) 74 list_page.append(temp) 75 76 77 78 # 尾頁 79 if self.current_page == self.all_page: 80 nex = '<a href = "javascript:void(0):">下一頁</a>' 81 else: 82 nex = '<a href = "/index/%s">下一頁</a>' % (self.current_page + 1,) 83 list_page.append(nex) 84 85 last = '<a href = "/index/%s">尾頁</a>'%(self.all_page) 86 list_page.append(last) 87 88 89 #跳轉 90 jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/') 91 script = ''' 92 <script> 93 function Jump(baseUrl,ths){ 94 var val = ths.previousElementSibling.value; 95 if (val.trim().length > 0){ 96 location.href = baseUrl + val; 97 } 98 } 99 </script> 100 ''' 101 list_page.append(jump) 102 list_page.append(script) 103 str_page = "".join(list_page) 104 105 return str_page 106 107 class IndexHandler(tornado.web.RequestHandler): 108 109 def get(self, page): 110 obj = Pagenation(page,len(LIST_INFO),'/index/') 111 current_list = LIST_INFO[obj.start:obj.end] 112 str_page = obj.string_pager() 113 self.render('index.html', list_info=current_list, current_page=obj.current_page, str_page=str_page) 114 115 application = tornado.web.Application([ 116 (r'/index/(?P<page>\d*)',IndexHandler) 117 118 ]) 119 120 121 if __name__ == "__main__": 122 application.listen(8080) 123 tornado.ioloop.IOLoop.instance().start()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 .pager a{ 8 display: inline-block; 9 padding: 5px 6px; 10 margin: 10px 3px; 11 border: 1px solid #2b669a; 12 text-decoration:none; 13 14 } 15 .pager a.active{ 16 background-color: #2b669a; 17 color: white; 18 } 19 </style> 20 </head> 21 <body> 22 <h3>顯示數據</h3> 23 <table border="1"> 24 <thead> 25 <tr> 26 <th>用戶名</th> 27 <th>郵箱</th> 28 </tr> 29 </thead> 30 <tbody> 31 {% for line in list_info %} 32 <tr> 33 <td>{{line['username']}}</td> 34 <td>{{line['email']}}</td> 35 </tr> 36 {% end %} 37 </tbody> 38 </table> 39 <div class="pager"> 40 {% raw str_page %} 41 </div> 42 </body> 43 </html>
注:兩個文件必須放在同一個文件夾下,中間前端代碼中有用到XSS攻擊和模板語言,這兩個知識點下面會詳細解釋
Tornao中的模板語言和django中相似,模板引擎將模板文件載入內存,而後將數據嵌入其中,最終獲取到一個完整的字符串,再將字符串返回給請求者。
Tornado 的模板支持「控制語句」和「表達語句」,控制語句是使用 {%
和 %}
包起來的 例如 {% if len(items) > 2 %}
。表達語句是使用 {{
和 }}
包起來的,例如 {{ items[0] }}
。
控制語句和對應的 Python 語句的格式基本徹底相同。咱們支持 if
、for
、while
和 try
,這些語句邏輯結束的位置須要用 {% end %}
作標記。還經過 extends
和 block
語句實現了模板繼承。這些在 template
模塊 的代碼文檔中有着詳細的描述。
注:在使用模板前須要在setting中設置模板路徑:"template_path" : "views"
1 settings = { 2 'template_path':'views', #設置模板路徑,設置完能夠把HTML文件放置views文件夾中
3 'static_path':'static', # 設置靜態模板路徑,設置完能夠把css,JS,Jquery等靜態文件放置static文件夾中
4 'static_url_prefix': '/sss/', #導入時候須要加上/sss/,例如<script src="/sss/jquery-1.9.1.min.js"></script>
5 'cookie_secret': "asdasd", #cookie生成祕鑰時候需提早生成隨機字符串,須要在這裏進行渲染
6 'xsrf_cokkies':True, #容許CSRF使用
7 } 8 application = tornado.web.Application([ 9 (r'/index',IndexHandler), 10 ],**settings) #須要在這裏加載
一、模板語言基本使用for循環,if..else使用,自定義UIMethod以UIModule
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import tornado.ioloop 4 import tornado.web 5 import uimodule as md 6 import uimethod as mt 7 8 INPUT_LIST = [] 9 class MainHandler(tornado.web.RequestHandler): 10 def get(self, *args, **kwargs): 11 name = self.get_argument('xxx',None) 12 if name: 13 INPUT_LIST.append(name) 14 self.render("index.html",npm = "NPM88888",xxoo = INPUT_LIST) 15 16 def post(self, *args, **kwargs): 17 name = self.get_argument('xxx') 18 INPUT_LIST.append(name) 19 self.render("index.html", npm = "NPM88888", xxoo = INPUT_LIST) 20 # self.write("Hello, World!!!") 21 22 settings = { 23 'template_path':'tpl', # 模板路徑的配置 24 'static_path':'statics', # 靜態文件路徑的配置 25 'ui_methods':mt, # 自定義模板語言 26 'ui_modules':md, # 自定義模板語言 27 } 28 29 #路由映射,路由系統 30 application = tornado.web.Application([ 31 (r"/index",MainHandler), 32 ],**settings) 33 34 35 if __name__ == "__main__": 36 # 運行socket 37 application.listen(8000) 38 tornado.ioloop.IOLoop.instance().start()
1 from tornado.web import UIModule 2 from tornado import escape 3 4 class custom(UIModule): 5 6 def render(self, *args, **kwargs): 7 return "張巖林"
1 def func(self,arg): 2 return arg.lower()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link type="text/css" rel="stylesheet" href="static/commons.css"> 7 </head> 8 <body> 9 <script src="static/zhang.js"></script> 10 <h1>Hello world</h1> 11 <h1>My name is zhangyanlin</h1> 12 <h1>輸入內容</h1> 13 <form action="/index" method="post"> 14 <input type="text" name="xxx"> 15 <input type="submit" value="提交"> 16 </form> 17 <h1>展現內容</h1> 18 <h3>{{ npm }}</h3> 19 <h3>{{ func(npm)}}</h3> 20 <h3>{% module custom() %}</h3> 21 <ul> 22 {% for item in xxoo %} 23 {% if item == "zhangyanlin" %} 24 <li style="color: red">{{item}}</li> 25 {% else %} 26 <li>{{item}}</li> 27 {% end %} 28 {% end %} 29 </ul> 30 </body> 31 </html>
二、母板繼承
(1)、至關於python的字符串格式化同樣,先定義一個佔位符
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 5 <title>帥哥</title> 6 <link href="{{static_url("css/common.css")}}" rel="stylesheet" /> 7 {% block CSS %}{% end %} 8 </head> 9 <body> 10 11 <div class="pg-header"> 12 13 </div> 14 15 {% block RenderBody %}{% end %} 16 17 <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> 18 19 {% block JavaScript %}{% end %} 20 </body> 21 </html>
(2)、再子板中相應的位置繼承模板的格式
1 {% extends 'layout.html'%} 2 {% block CSS %} 3 <link href="{{static_url("css/index.css")}}" rel="stylesheet" /> 4 {% end %} 5 6 {% block RenderBody %} 7 <h1>Index</h1> 8 9 <ul> 10 {% for item in li %} 11 <li>{{item}}</li> 12 {% end %} 13 </ul> 14 15 {% end %} 16 17 {% block JavaScript %} 18 19 {% end %}
1 <div> 2 <ul> 3 <li>張巖林帥</li> 4 <li>張巖林很帥</li> 5 <li>張巖林很很帥</li> 6 </ul> 7 </div>
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 5 <title>張巖林</title> 6 <link href="{{static_url("css/common.css")}}" rel="stylesheet" /> 7 </head> 8 <body> 9 10 <div class="pg-header"> 11 {% include 'header.html' %} 12 </div> 13 14 <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> 15 16 </body> 17 </html>
1 在模板中默認提供了一些函數、字段、類以供模板使用: 2
3 escape: tornado.escape.xhtml_escape 的別名 4 xhtml_escape: tornado.escape.xhtml_escape 的別名 5 url_escape: tornado.escape.url_escape 的別名 6 json_encode: tornado.escape.json_encode 的別名 7 squeeze: tornado.escape.squeeze 的別名 8 linkify: tornado.escape.linkify 的別名 9 datetime: Python 的 datetime 模組 10 handler: 當前的 RequestHandler 對象 11 request: handler.request 的別名 12 current_user: handler.current_user 的別名 13 locale: handler.locale 的別名 14 _: handler.locale.translate 的別名 15 static_url: for handler.static_url 的別名 16 xsrf_form_html: handler.xsrf_form_html 的別名
當你製做一個實際應用時,你會須要用到 Tornado 模板的全部功能,尤爲是 模板繼承功能。全部這些功能均可以在template
模塊 的代碼文檔中瞭解到。(其中一些功能是在 web
模塊中實現的,例如 UIModules
)
Cookie,有時也用其複數形式Cookies,指某些網站爲了辨別用戶身份、進行session跟蹤而儲存在用戶本地終端上的數據(一般通過加密)。定義於RFC2109和2965都已廢棄,最新取代的規範是RFC6265。(能夠叫作瀏覽器緩存)
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 import tornado.ioloop 5 import tornado.web 6
7
8 class MainHandler(tornado.web.RequestHandler): 9 def get(self): 10 print(self.cookies) # 獲取全部的cookie
11 self.set_cookie('k1','v1') # 設置cookie
12 print(self.get_cookie('k1')) # 獲取指定的cookie
13 self.write("Hello, world") 14
15 application = tornado.web.Application([ 16 (r"/index", MainHandler), 17 ]) 18
19
20 if __name__ == "__main__": 21 application.listen(8888) 22 tornado.ioloop.IOLoop.instance().start()
Cookie 很容易被惡意的客戶端僞造。加入你想在 cookie 中保存當前登錄用戶的 id 之類的信息,你須要對 cookie 做簽名以防止僞造。Tornado 經過 set_secure_cookie 和 get_secure_cookie 方法直接支持了這種功能。 要使用這些方法,你須要在建立應用時提供一個密鑰,名字爲 cookie_secret。 你能夠把它做爲一個關鍵詞參數傳入應用的設置中
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 import tornado.ioloop 5 import tornado.web 6
7 class MainHandler(tornado.web.RequestHandler): 8 def get(self): 9 if not self.get_secure_cookie("mycookie"): # 獲取帶簽名的cookie
10 self.set_secure_cookie("mycookie", "myvalue") # 設置帶簽名的cookie
11 self.write("Your cookie was not set yet!") 12 else: 13 self.write("Your cookie was set!") 14 application = tornado.web.Application([ 15 (r"/index", MainHandler), 16 ]) 17
18 if __name__ == "__main__": 19 application.listen(8888) 20 tornado.ioloop.IOLoop.instance().start()
簽名Cookie的本質是:
寫cookie過程: 將值進行base64加密 對除值之外的內容進行簽名,哈希算法(沒法逆向解析) 拼接 簽名 + 加密值 讀cookie過程: 讀取 簽名 + 加密值 對簽名進行驗證 base64解密,獲取值內容
用cookie作簡單的自定義用戶驗證,下面會寫一個絕對牛逼的自定義session用戶驗證
1 #!/usr/bin/python 2 # -*- coding: UTF-8 -*- 3 4 5 import tornado.web 6 import tornado.ioloop 7 container = {} 8 # container = { 9 # # "第一我的的隨機字符串":{}, 10 # # "第一我的的隨機字符串":{'k1': 111, 'parents': '你'}, 11 # } 12 13 class Session: 14 def __init__(self, handler): 15 self.handler = handler 16 self.random_str = None 17 18 def __genarate_random_str(self): 19 import hashlib 20 import time 21 obj = hashlib.md5() 22 obj.update(bytes(str(time.time()), encoding='utf-8')) 23 random_str = obj.hexdigest() 24 return random_str 25 26 def __setitem__(self, key,value): 27 # 在container中加入隨機字符串 28 # 定義專屬於本身的數據 29 # 在客戶端中寫入隨機字符串 30 # 判斷,請求的用戶是否已有隨機字符串 31 if not self.random_str: 32 random_str = self.handler.get_cookie('__kakaka__') 33 if not random_str: 34 random_str = self.__genarate_random_str() 35 container[random_str] = {} 36 else: 37 # 客戶端有隨機字符串 38 if random_str in container.keys(): 39 pass 40 else: 41 random_str = self.__genarate_random_str() 42 container[random_str] = {} 43 self.random_str = random_str # self.random_str = asdfasdfasdfasdf 44 45 container[self.random_str][key] = value 46 self.handler.set_cookie("__kakaka__", self.random_str) 47 48 def __getitem__(self,key): 49 # 獲取客戶端的隨機字符串 50 # 從container中獲取專屬於個人數據 51 # 專屬信息【key】 52 random_str = self.handler.get_cookie("__kakaka__") 53 if not random_str: 54 return None 55 # 客戶端有隨機字符串 56 user_info_dict = container.get(random_str,None) 57 if not user_info_dict: 58 return None 59 value = user_info_dict.get(key, None) 60 return value 61 class BaseHandler(tornado.web.RequestHandler): 62 def initialize(self): 63 self.session = Session(self) 64 65 class IndexHandler(BaseHandler): 66 def get(self): 67 if self.get_argument('u',None) in ['alex','eric']: 68 self.session['is_login'] = True 69 self.session['name'] = self.get_argument('u',None) 70 71 else: 72 self.write('請登陸') 73 74 class ManagerHandler(BaseHandler): 75 def get(self): 76 s = Session(self) 77 val = self.session['is_login'] 78 if val: 79 self.write(self.session['name']) 80 else: 81 self.write('失敗')
因爲Cookie保存在瀏覽器端,因此在瀏覽器端也可使用JavaScript來操做Cookie。
1 /*
2 設置cookie,指定秒數過時, 3 name表示傳入的key, 4 value表示傳入相對應的value值, 5 expires表示當前日期在加5秒過時 6 */
7
8 function setCookie(name,value,expires){ 9 var temp = []; 10 var current_date = new Date(); 11 current_date.setSeconds(current_date.getSeconds() + 5); 12 document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString(); 13 }
注:jQuery中也有指定的插件 jQuery Cookie 專門用於操做cookie,猛擊這裏
原本這想新開一個帖子,可是仍是把代碼貼在這吧,有用到session驗證的時候直接複製拿走就好
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import tornado.web 4 import tornado.ioloop 5 6 container = {} 7 class Session: 8 def __init__(self, handler): 9 self.handler = handler 10 self.random_str = None 11 12 def __genarate_random_str(self): 13 import hashlib 14 import time 15 obj = hashlib.md5() 16 obj.update(bytes(str(time.time()), encoding='utf-8')) 17 random_str = obj.hexdigest() 18 return random_str 19 20 def __setitem__(self, key, value): 21 # 在container中加入隨機字符串 22 # 定義專屬於本身的數據 23 # 在客戶端中寫入隨機字符串 24 # 判斷,請求的用戶是否已有隨機字符串 25 if not self.random_str: 26 random_str = self.handler.get_cookie('__session__') 27 if not random_str: 28 random_str = self.__genarate_random_str() 29 container[random_str] = {} 30 else: 31 # 客戶端有隨機字符串 32 if random_str in container.keys(): 33 pass 34 else: 35 random_str = self.__genarate_random_str() 36 container[random_str] = {} 37 self.random_str = random_str # self.random_str = asdfasdfasdfasdf 38 39 container[self.random_str][key] = value 40 self.handler.set_cookie("__session__", self.random_str) 41 42 def __getitem__(self, key): 43 # 獲取客戶端的隨機字符串 44 # 從container中獲取專屬於個人數據 45 # 專屬信息【key】 46 random_str = self.handler.get_cookie("__session__") 47 if not random_str: 48 return None 49 # 客戶端有隨機字符串 50 user_info_dict = container.get(random_str,None) 51 if not user_info_dict: 52 return None 53 value = user_info_dict.get(key, None) 54 return value 55 56 57 class BaseHandler(tornado.web.RequestHandler): 58 def initialize(self): 59 self.session = Session(self)
跨站腳本攻擊(Cross Site Scripting),爲不和層疊樣式表(Cascading Style Sheets, CSS)的縮寫混淆,故將跨站腳本攻擊縮寫爲XSS。惡意攻擊者往Web頁面裏插入惡意Script代碼,當用戶瀏覽該頁之時,嵌入其中Web裏面的Script代碼會被執行,從而達到惡意攻擊用戶的特殊目的。
tornado中已經爲咱們給屏蔽了XSS,可是當咱們後端向前端寫前端代碼的時候傳入瀏覽器是字符串,而不是造成代碼格式。因此就須要一個反解,在傳入模板語言中前面加一個raw,例如{{ raw zhangyanlin }},這樣通俗的講可能不太懂,寫一段代碼,可能就懂了
1 class IndexHandler(tornado.web.RequestHandler): 2 def get(self, *args, **kwargs): 3 jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/') 4 script = ''' 5 <script> 6 function Jump(baseUrl,ths){ 7 var val = ths.previousElementSibling.value; 8 if (val.trim().length > 0){ 9 location.href = baseUrl + val; 10 } 11 } 12 </script> 13 ''' 14 self.render('index.html',jump=jump,script=script) #傳入兩個前端代碼的字符串
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <form action="/csrf" method="post"> 9 {% raw xsrf_form_html() %} 10 <p><input name="user" type="text" placeholder="用戶"/></p> 11 <p><input name='pwd' type="text" placeholder="密碼"/></p> 12 <input type="submit" value="Submit" /> 13 <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" /> 14 </form> 15 16 <script src="/statics/jquery-1.12.4.js"></script> 17 <script type="text/javascript"> 18 19 function ChangeCode() { 20 var code = document.getElementById('imgCode'); 21 code.src += '?'; 22 } 23 function getCookie(name) { 24 var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); 25 return r ? r[1] : undefined; 26 } 27 28 function SubmitCsrf() { 29 var nid = getCookie('_xsrf'); 30 $.post({ 31 url: '/csrf', 32 data: {'k1': 'v1',"_xsrf": nid}, 33 success: function (callback) { 34 // Ajax請求發送成功有,自動執行 35 // callback,服務器write的數據 callback=「csrf.post」 36 console.log(callback); 37 } 38 }); 39 } 40 </script> 41 </body> 42 </html>
簡單來講就是在form驗證裏面生成了一段相似於本身的身份證號同樣,攜帶着他來訪問網頁
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 import tornado.web 5 import tornado.ioloop 6 import os 7 8 class IndexHandler(tornado.web.RequestHandler): 9 def get(self, *args, **kwargs): 10 self.render('index.html') 11 12 def post(self, *args, **kwargs): 13 file_metas = self.request.files["filename"] # 獲取文件信息 14 for meta in file_metas: 15 file_name = meta['filename'] # 得到他的文件名字 16 file_names = os.path.join('static','img',file_name) 17 with open(file_names,'wb') as up: # 打開本地一個文件 18 up.write(meta['body']) # body就是文件內容,把他寫到本地 19 20 settings = { 21 'template_path':'views', 22 'static_path':'static', 23 'static_url_prefix': '/statics/', 24 } 25 26 application = tornado.web.Application([ 27 (r'/index',IndexHandler) 28 ],**settings) 29 30 if __name__ == '__main__': 31 application.listen(8888) 32 tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上傳文件</title> </head> <body> <form action="/index" method="post" enctype="multipart/form-data"> <input type="file" name = "filename"> <input type="submit" value="提交"> </form> </body> </html>
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" > 9 <div id="main"> 10 <input name="filename" id="my_file" type="file" /> 11 <input type="button" name="action" value="Upload" onclick="redirect()"/> 12 <iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe> 13 </div> 14 </form> 15 16 <script> 17 function redirect(){ 18 document.getElementById('my_iframe').onload = Testt; 19 document.getElementById('my_form').target = 'my_iframe'; 20 document.getElementById('my_form').submit(); 21 22 } 23 24 function Testt(ths){ 25 var t = $("#my_iframe").contents().find("body").text(); 26 console.log(t); 27 } 28 </script> 29 </body> 30 </html>
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <input type="file" id="img" /> 9 <input type="button" onclick="UploadFile();" value="提交"/> 10 11 <script src="/statics/jquery-1.12.4.js"></script> 12 <script> 13 function UploadFile(){ 14 var fileObj = $("#img")[0].files[0]; 15 var form = new FormData(); 16 form.append("filename", fileObj); 17 18 $.ajax({ 19 type:'POST', 20 url: '/index', 21 data: form, 22 processData: false, // tell jQuery not to process the data 23 contentType: false, // tell jQuery not to set contentType 24 success: function(arg){ 25 console.log(arg); 26 } 27 }) 28 } 29 </script> 30 </body> 31 </html>
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <input type="file" id="img" /> 9 <input type="button" onclick="UploadFile();" value="提交" /> 10 <script> 11 function UploadFile(){ 12 var fileObj = document.getElementById("img").files[0]; 13 14 var form = new FormData(); 15 form.append("filename", fileObj); 16 17 var xhr = new XMLHttpRequest(); 18 xhr.open("post", '/index', true); 19 xhr.send(form); 20 } 21 </script> 22 </body> 23 </html>
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 import tornado.web 5 import tornado.ioloop 6 import os 7 8 class IndexHandler(tornado.web.RequestHandler): 9 def get(self, *args, **kwargs): 10 self.render('index.html') 11 12 def post(self, *args, **kwargs): 13 file_metas = self.request.files["filename"] # 獲取文件信息 14 for meta in file_metas: 15 file_name = meta['filename'] # 得到他的文件名字 16 file_names = os.path.join('static','img',file_name) 17 with open(file_names,'wb') as up: # 打開本地一個文件 18 up.write(meta['body']) # body就是文件內容,把他寫到本地 19 20 settings = { 21 'template_path':'views', 22 'static_path':'static', 23 'static_url_prefix': '/statics/', 24 } 25 26 application = tornado.web.Application([ 27 (r'/index',IndexHandler) 28 ],**settings) 29 30 if __name__ == '__main__': 31 application.listen(8888) 32 tornado.ioloop.IOLoop.instance().start()
注:下面全部的實例用相同的python代碼都能實現,只須要改前端代碼,python代碼文件名爲start.py
用python生成隨機驗證碼須要借鑑一個插件,和一個io模塊,實現起來也很是容易,固然也須要借鑑session來判斷驗證碼是否錯誤,下面寫一段用戶登陸驗證帶驗證碼的,再看下效果,插件必須和執行文件必須放在更目錄下
1 複製代碼 2 #!/usr/bin/env python 3 # -*- coding:utf-8 -*- 4 import tornado.web 5 import tornado.ioloop 6 7 container = {} 8 class Session: 9 def __init__(self, handler): 10 self.handler = handler 11 self.random_str = None 12 13 def __genarate_random_str(self): 14 import hashlib 15 import time 16 obj = hashlib.md5() 17 obj.update(bytes(str(time.time()), encoding='utf-8')) 18 random_str = obj.hexdigest() 19 return random_str 20 21 def __setitem__(self, key, value): 22 # 在container中加入隨機字符串 23 # 定義專屬於本身的數據 24 # 在客戶端中寫入隨機字符串 25 # 判斷,請求的用戶是否已有隨機字符串 26 if not self.random_str: 27 random_str = self.handler.get_cookie('__session__') 28 if not random_str: 29 random_str = self.__genarate_random_str() 30 container[random_str] = {} 31 else: 32 # 客戶端有隨機字符串 33 if random_str in container.keys(): 34 pass 35 else: 36 random_str = self.__genarate_random_str() 37 container[random_str] = {} 38 self.random_str = random_str # self.random_str = asdfasdfasdfasdf 39 40 container[self.random_str][key] = value 41 self.handler.set_cookie("__session__", self.random_str) 42 43 def __getitem__(self, key): 44 # 獲取客戶端的隨機字符串 45 # 從container中獲取專屬於個人數據 46 # 專屬信息【key】 47 random_str = self.handler.get_cookie("__session__") 48 if not random_str: 49 return None 50 # 客戶端有隨機字符串 51 user_info_dict = container.get(random_str,None) 52 if not user_info_dict: 53 return None 54 value = user_info_dict.get(key, None) 55 return value 56 57 58 class BaseHandler(tornado.web.RequestHandler): 59 def initialize(self): 60 self.session = Session(self) 61 62 63 class LoginHandler(BaseHandler): 64 def get(self, *args, **kwargs): 65 self.render('login.html' ,state = "") 66 67 def post(self, *args, **kwargs): 68 username = self.get_argument('username') 69 password = self.get_argument('password') 70 code =self.get_argument('code') 71 check_code = self.session['CheckCode'] 72 if username =="zhangyanlin" and password == "123" and code.upper() == check_code.upper(): 73 self.write("登陸成功") 74 else: 75 self.render('login.html',state = "驗證碼錯誤") 76 77 class CheckCodeHandler(BaseHandler): 78 def get(self, *args, **kwargs): 79 import io 80 import check_code 81 mstream = io.BytesIO() 82 img,code = check_code.create_validate_code() 83 img.save(mstream,"GIF") 84 self.session['CheckCode'] =code 85 self.write(mstream.getvalue()) 86 87 88 settings = { 89 'template_path':'views', 90 'cookie_secret': "asdasd", 91 } 92 93 application = tornado.web.Application([ 94 (r'/login',LoginHandler), 95 (r'/check_code',CheckCodeHandler) 96 ],**settings) 97 98 if __name__ == '__main__': 99 application.listen(8888) 100 tornado.ioloop.IOLoop.instance().start()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>驗證碼</title> 6 </head> 7 <body> 8 <form action="/login" method="post"> 9 <p>用戶名: <input type="text" name="username"> </p> 10 <p>密碼: <input type="password" name="password"> </p> 11 <p>驗證碼: <input type="text" name="code"><img src="/check_code" onclick="ChangeCode();" id = "checkcode"></p> 12 <input type="submit" value="submit"> <span>{{state}}</span> 13 </form> 14 <script type="text/javascript"> //當點擊圖片的時候,會刷新圖片,這一段代碼就能夠實現 15 function ChangeCode() { 16 var code = document.getElementById('checkcode'); 17 code.src += "?"; 18 } 19 </script> 20 </body> 21 </html>
插件下載地址:猛擊這裏