tornado web 框架的认识
tornado 简介
1,概述
Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对epoll的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个 理想框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅The C10K problem)
Tornado代表嵌入实时应用中最新一代的开发和执行环境。 Tornado 包含三个完整的部分:
(1)、Tornado系列工具, 一套位于主机或目标机上强大的交互式开发工具和使用程序;
(2)、VxWorks 系统, 目标板上高性能可扩展的实时操作系统;
(3)、可选用的连接主机和目标机的通讯软件包 如以太网、串行线、在线仿真器或ROM仿真器
2,tornado特点
Tornado的独特之处在于其所有开发工具能够使用在应用开发的任意阶段以及任何档次的硬件资源上。而且,完整集的Tornado工具可以使开发人员完全不用考虑与目标连接的策略或目标存储区大小。Tornado 结构的专门设计为开发人员和第三方工具厂商提供了一个开放环境。已有部分应用程序接口可以利用并附带参考书目,内容从开发环境接口到连接实现。Tornado包括强大的开发和调试工具,尤其适用于面对大量问题的嵌入式开发人员。这些工具包括C和C++源码级别的调试器,目标和工具管理,系统目标跟踪,内存使用分析和自动配置. 另外,所有工具能很方便地同时运行,很容易增加和交互式开发。
3,tornado模块索引
最重要的一个模块是web
, 它就是包含了 Tornado 的大部分主要功能的 Web 框架。其它的模块都是工具性质的, 以便让 web
模块更加有用 后面的 Tornado 攻略 详细讲解了 web
模块的使用方法。
主要模块
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 循环
tornado 框架使用
1,安装tornado
pip install tornado
源码安装:https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
2、先写一个入门级的代码吧,相信大家都能看懂,声明:tornado内部已经帮我们实现socket。
#!/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 端口
第二步:浏览器客户端访问 /index --> http://127.0.0.1:8080/index
第三步:服务器接受请求,并交由对应的类处理该请求
第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
第五步:然后将类的方法返回给浏览器
tornado 路由系统
在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攻击和模板语言,这两个知识点下面会详细解释
tornado模板引擎
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) #需要在这里加载
1、模板语言基本使用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>
2、母板继承
(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 %}
3、导入内容
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
)
tornado cookie
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。定义于RFC2109和2965都已废弃,最新取代的规范是RFC6265。(可以叫做浏览器缓存)
1、cookie的基本操作
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()
2、加密cookie(签名)
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('失败')
3、JavaScript操作Cookie
由于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
cookie 和sesson的区别
1,cookie数据存放在客户的浏览器上,sesson数据存放在服务器上。
2,cookie不是很安全,别人可以分析存放本地的cookie并进行cookie欺骗,考虑到安全应当使用session
3,session会在一定的时间保存在服务器上。当访问量增多会比较粘你服务器的性能 考虑到减少服务器性能方面应当使用cookie
4,单个cookie保存的数据不能超过4k 横多浏览器都限制一个站点包村20个cookie
5,所以个人建议,将登录的信息的等重要的信息存放在session,其他信息如需保留可以存放在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)
xss攻击和csrf请求伪造
跨站脚本攻击(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验证里面生成了一段类似于自己的身份证号一样,携带着他来访问网页
tornado上传文件
上传文件这块可以分为两大类,第一类是通过form表单验证进行上传,还有一类就是通过ajax上传,下面就来介绍一下这两类
1、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>
2.ajax上传文件
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
tornado生成随机字符串
用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>
插件下载地址:猛击这里