跨站请求伪造CSRF
开启xsrf(就是叫法不一样和csrf一样),'xsrf_cookies':True
settings = { 'template_path':'template', 'static_path':'static', 'static_path_prefix':'/static/', 'xsrf_cookies':True, }
在post表单中增加csrf认证
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>index.html</h1> <h1>{{ name }}</h1> <form action="/index" method="post">
{% module xsrf_form_html() %} <p>user:<input type="text"/></p> <p>password:<input type="password" /> </p> <input type="submit" value="submit" /> </form> </body> </html>
网站请求效果(表单中没有增加认证token):
加上token
AJAX方法
官方提供:
获取cookie的token信息 args._xsrf = getCookie("_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 + ")")); }}); };
UI(自定义标签)
自定义有两种方式:uimethods(方法)和uimodule(模块)
创建玩一下uimethods,新创建一个py文件
#_*_coding:utf-8_*_ def abcd(self): return '自定义uimethod方法'
主文件
#_*_coding:utf-8_*_ import tornado.ioloop import tornado.web
#导入UImethod import uimethods #'ui_methods':uimethods 设置下即可 settings = { 'template_path':'template', 'static_path':'static', 'static_path_prefix':'/static/', 'ui_methods':uimethods } class MainHandler(tornado.web.RequestHandler): def get(self): dic={'name':'abc'} self.render('index.html',**dic) app=tornado.web.Application([ (r"/index", MainHandler), ],**settings) if __name__ == "__main__": app.listen(8888) tornado.ioloop.IOLoop.current().start()
html页面中加入自定方法即可
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>index.html</h1> //自定义方法 {{abcd()}} </body> </html>
演示效果
自定义uimedule(代码改动不多)
html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>index.html</h1> <p>{{abcd()}}</p> <p>自定义module</p> //这里可以传递参数 {% module test_mod(123) %} </body> </html>
uimodules文件
#_*_coding:utf-8_*_ from tornado.web import UIModule class test_mod(UIModule): #render是死的 最后返回的就是这个方法 def render(self, *args, **kwargs): return 'uimodule.', args
主文件
#_*_coding:utf-8_*_ import tornado.ioloop import tornado.web import uimethods import uimodules settings = { 'template_path':'template', 'static_path':'static', 'static_path_prefix':'/static/', 'ui_methods':uimethods, 'ui_modules':uimodules, } class MainHandler(tornado.web.RequestHandler): def get(self): dic={'name':'abc'} self.render('index.html',**dic) app=tornado.web.Application([ (r"/index", MainHandler), ],**settings) if __name__ == "__main__": app.listen(8888) tornado.ioloop.IOLoop.current().start()
运行效果:
自定义当方法在以后的应用是很广泛的,tornado 的方法不熟悉,完全可以用这个方法自己写
用户认证
简单说一下session和cookie 关系
简图:
上图可以看出cookie中不会存在敏感信息,重要的信息存储在服务器端的session中
Tornado是没有session 的,如下图这么处理的
这个方法不怎么安全,连接Tornado机制的就很容易伪造
实现tornado源生验证(很少会用这个方式来完成登陆验证)
主文件
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): #从cookie中获取key=login_user的值 login_user = self.get_secure_cookie("login_user", None) if login_user: #获取cookie成功就打印这个登陆名 self.write(login_user) else: self.redirect('/login') class LoginHandler(tornado.web.RequestHandler): def get(self): #self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): #获取前端传递过来的 name和password username = self.get_argument('username') password = self.get_argument('password') if username == 'lily0912' and password == '123': #登陆成功就设置cookie,key=login_user,值=lily0912 self.set_secure_cookie('login_user', 'lily0912') #跳转到首页 self.redirect('/index') else: self.render('login.html', **{'status': 'name or password error!!!!'}) settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'asdassdasdsd123' } application = tornado.web.Application([ (r"/index", MainHandler), (r"/login", LoginHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>登陆</h1> <form action="/login" method="post"> <p>user:<input name="username" type="text"/></p> <p>password:<input name="password" type="password" /> </p> <input type="submit" value="submit" /> </form> </body> </html>
验证:
登陆成功后跳转页面,然后可以看到服务器给客户端的cookie信息。
自定义session验证
写cookie过程:
- 将值进行base64加密
- 对除值意外的内容进行签名,哈希算法(无法逆向解析)
- 拼接 签名 + 加密值
读cookie过程:
- 读取 签名 + 加密值
- 对签名进行验证
- base64解密,获取值内容
所以,我们会将base64加密的值返回给用户。而对于session,他只会将签名返回给用户,然后根据签名获取redis或数据库中保存的其他值。即:在session中,签名和session值的集合是一一对应的。
python魔法方法(为tornado 增加session功能就靠它了)
简单说下这个例子是python模仿字典工作
class Foo(object): def __init__(self, key, value): self.key = [] self.value = [] self.key.append(key) self.value.append(value) def __len__(self): return len(self.key) def __getitem__(self, item): try: __index = self.key.index(item) return self.value[__index] except ValueError: raise KeyError('can not find the key') def __setitem__(self, key, value): if key not in self.key: self.key.append(key) self.value.append(value) else: __index = self.key.index(key) self.value[__index] = value def __delitem__(self, key): try: __index = self.key.index(key) del self.key[__index] del self.value[__index] except ValueError: raise KeyError('can not find the key') def __str__(self): result_list = [] for index in xrange(len(self.key)): __key = self.key[index] __value = self.value[index] result = __key, __value result_list.append(result) return str(result_list) def __iter__(self): self.__index = 0 return self def next(self): if self.__index == len(self.key): self.__index = 0 raise StopIteration() else: __key = self.key[self.__index] __value = self.value[self.__index] result = __key, __value self.__index += 1 return result def __reversed__(self): __result = self.value[:] __result.reverse() return __result def __contains__(self, item): if item in self.value: return True else: return False a = Foo('scolia', 'good') a[123] = 321 a[456] = 654 a[789] = 987 print a.key print len(a) del a[789] print a for x, y in a: print x, y print reversed(a) print 123 in a print 321 in av
['scolia', 123, 456, 789] 4 [('scolia', 'good'), (123, 321), (456, 654)] scolia good 123 321 456 654 [654, 321, 'good'] False True
这个东西没有弄过的的熟悉一下。确实有的魔法的感觉。
Tornado 实现session
显示熟悉一下流程
application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings)
根据url匹配都会找到对应的类比如:class LoginHandler(tornado.web.RequestHandler)
都会继承这个tornado.web.RequestHandler父类,首先父类会初始化RequestHandler.__init__() ,最后还调用self.initialize(**kwargs)了这个类。这个里面什么都没有。主要是留给我扩展用的
最后执行LoginHandler 类中的方法
我们有做的就是在执行LoginHandler方法之前 生成session 就OK了。
可以这么做:
自己写个基类继承tornado.web.RequestHandler
class BaseHandler(tornado.web.RequestHandler): #继承后直接改写这个方法就行
def initialize(self):
在这里写session内容就OK了 pass 下面的子类直接继承BaseHandler class MainHandler(BaseHandler): class LoginHandler(BaseHandler):
OK现在开始实现session
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from hashlib import sha1 import os, time #生成session id session_id=lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest() class Session(object): def __init__(self,obj): self.obj=obj def __getitem__(self, key): pass def __setitem__(self, key, value): #创建session_id token=session_id() #设置cookie的 key这个无所谓,起个名就行 cookie_key='_session' self.obj.set_secure_cookie(cookie_key,token) def __delitem__(self, key): pass class BaseHandler(tornado.web.RequestHandler): def initialize(self): #将self传递给session方法,原因很简单Session方法本身无法将cookie写入LoginHandle对象中。因为它没有这个方法 self.mysesson=Session(self) class MainHandler(BaseHandler): def get(self): #从cookie中获取key=login_user的值 login_user = self.get_secure_cookie("login_user", None) if login_user: #获取cookie成功就打印这个登陆名 self.write(login_user) else: self.redirect('/login') #RequestHandler.__init__()实例化方法 class LoginHandler(BaseHandler): def get(self): #self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('username') password = self.get_argument('password') if username == 'lily0912' and password == '123': #self.set_secure_cookie('login_user', 'lily0912') #成功登陆就设置状态True self.mysesson['login_status']='True' self.redirect('/index') else: self.render('login.html', **{'status': 'name or password error!!!!'}) settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'asdassdasdsd123' } application = tornado.web.Application([ (r"/index", MainHandler), (r"/login", LoginHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
看下session是否写入成功