web 框架4 cookie session再次介绍 tornado自定义组件 session验证 图片验证码组件 csrf xss等介绍 文件上传的方式
1 cookie介绍1 cookie验证机制 cookie的写入方式两种
本质为在浏览器端保存的键值对,由服务端写在浏览器端,以后每次请求的时候,浏览器都携带着cookie来访问,cookies的使用之处非常多,比如用户验证,登陆界面,右侧菜单隐藏,控制页面列表显示条数等,已经后面的session都是基于cookie的。cookie从设置方面来说可以由tronado和前端js设置
tornado设置(普通字符串,tronado做了分割等处理)
self.cookies
self.get_cookie('k1')
self.set_cookie('k2', 'v2')
浏览器js设置
document.cookie
document.cookie.split(";") 获取所有的cookie列表
document.cookie = "k3=66" 设置
document.cookie = "k3=66;path='/"
1.0 如果有人问你什么是cookie ,cookie和session区别是什么
如下=====start
cookie : 存在于浏览器的键值对,用于验证服务端身份信息的 存放的值是一个随机字符串(token) 下次会带着这个token来,说明你的身份 session : 数据信息 可以放在内存,缓存,数据库 是一个验证机制,依赖于cookie 用于保存用户的敏感身份信息 如果服务器性能 可以将一些不重要的信息放在cookie 生成随机字符串: 服务端 ======= 下面的 为正规 cookie session区别: cookie放在客户端 session的用户信息放在内存,缓存,数据库 cookie session联系: session的数据信息是我们人为创建的。 是依据cookie创建的。 用户来了给用户一个token,下次用户来还带着这个token
如上=====end
1.1 签名cookie本质
签名cookie
Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_secure_cookie("mycookie"): self.set_secure_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!") application = tornado.web.Application([ (r"/", MainHandler), ], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=") 还可以写成 : settings = { cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=" } application = tornado.web.Application([ (r"/", MainHandler), ], **settings)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def _create_signature_v1(secret, *parts): hash = hmac.new(utf8(secret), digestmod=hashlib.sha1) for part in parts: hash.update(utf8(part)) return utf8(hash.hexdigest()) # 加密 def _create_signature_v2(secret, s): hash = hmac.new(utf8(secret), digestmod=hashlib.sha256) hash.update(utf8(s)) return utf8(hash.hexdigest()) def create_signed_value(secret, name, value, version=None, clock=None, key_version=None): if version is None: version = DEFAULT_SIGNED_VALUE_VERSION if clock is None: clock = time.time timestamp = utf8(str(int(clock()))) value = base64.b64encode(utf8(value)) if version == 1: signature = _create_signature_v1(secret, name, value, timestamp) value = b"|".join([value, timestamp, signature]) return value elif version == 2: # The v2 format consists of a version number and a series of # length-prefixed fields "%d:%s", the last of which is a # signature, all separated by pipes. All numbers are in # decimal format with no leading zeros. The signature is an # HMAC-SHA256 of the whole string up to that point, including # the final pipe. # # The fields are: # - format version (i.e. 2; no length prefix) # - key version (integer, default is 0) # - timestamp (integer seconds since epoch) # - name (not encoded; assumed to be ~alphanumeric) # - value (base64-encoded) # - signature (hex-encoded; no length prefix) def format_field(s): return utf8("%d:" % len(s)) + utf8(s) to_sign = b"|".join([ b"2", format_field(str(key_version or 0)), format_field(timestamp), format_field(name), format_field(value), b'']) if isinstance(secret, dict): assert key_version is not None, 'Key version must be set when sign key dict is used' assert version >= 2, 'Version must be at least 2 for key version support' secret = secret[key_version] signature = _create_signature_v2(secret, to_sign) return to_sign + signature else: raise ValueError("Unsupported version %d" % version) # 解密 def _decode_signed_value_v1(secret, name, value, max_age_days, clock): parts = utf8(value).split(b"|") if len(parts) != 3: return None signature = _create_signature_v1(secret, name, parts[0], parts[1]) if not _time_independent_equals(parts[2], signature): gen_log.warning("Invalid cookie signature %r", value) return None timestamp = int(parts[1]) if timestamp < clock() - max_age_days * 86400: gen_log.warning("Expired cookie %r", value) return None if timestamp > clock() + 31 * 86400: # _cookie_signature does not hash a delimiter between the # parts of the cookie, so an attacker could transfer trailing # digits from the payload to the timestamp without altering the # signature. For backwards compatibility, sanity-check timestamp # here instead of modifying _cookie_signature. gen_log.warning("Cookie timestamp in future; possible tampering %r", value) return None if parts[1].startswith(b"0"): gen_log.warning("Tampered cookie %r", value) return None try: return base64.b64decode(parts[0]) except Exception: return None def _decode_fields_v2(value): def _consume_field(s): length, _, rest = s.partition(b':') n = int(length) field_value = rest[:n] # In python 3, indexing bytes returns small integers; we must # use a slice to get a byte string as in python 2. if rest[n:n + 1] != b'|': raise ValueError("malformed v2 signed value field") rest = rest[n + 1:] return field_value, rest rest = value[2:] # remove version number key_version, rest = _consume_field(rest) timestamp, rest = _consume_field(rest) name_field, rest = _consume_field(rest) value_field, passed_sig = _consume_field(rest) return int(key_version), timestamp, name_field, value_field, passed_sig def _decode_signed_value_v2(secret, name, value, max_age_days, clock): try: key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value) except ValueError: return None signed_string = value[:-len(passed_sig)] if isinstance(secret, dict): try: secret = secret[key_version] except KeyError: return None expected_sig = _create_signature_v2(secret, signed_string) if not _time_independent_equals(passed_sig, expected_sig): return None if name_field != utf8(name): return None timestamp = int(timestamp) if timestamp < clock() - max_age_days * 86400: # The signature has expired. return None try: return base64.b64decode(value_field) except Exception: return None def get_signature_key_version(value): value = utf8(value) version = _get_version(value) if version < 2: return None try: key_version, _, _, _, _ = _decode_fields_v2(value) except ValueError: return None return key_version 内部算法
个人理解的签名cookie
COOKIE: cookie是存放在浏览器的键值对。 可以用来做用户验证,但是保存在浏览器的信息太多,所以后面很多用户信息放在服务端,浏览器是一个 随机字符串加作为验证信息。 cookie验证机制 服务端 设置cookie验证的时候 (比较靠谱的验证,而不用担心被人一眼看到浏览器觉得cookie就能请求) 1传入值x在tornado内部用base64加密加密后的值比如说是y, 即 y = base64(x) 比如说将这个y作为要发送给浏览器的cookie值得一部分,然后用|分割。 2 时间戳也作为要写入浏览器的cookie值得一部分,然后用|分割。 3 然后将 y值 时间戳, 还有自定义秘钥这三部分进行了单向加密 md5 或者hmac 作为要写入浏览器的cookie值得一部分,然后用|分割。 如上三部分 用|分割作为写入浏览器的cookie的value 浏览器再次请求的时候就会带着这个cookie信息来验证, 服务端会将y值根据base64解密成一个值跟自己的x进行对比,然后,根据服务端自己的y值,自己的时间戳 自己的自定义秘钥这三个再次单向加密md5或者hmac 与浏览器传过来的cookie的除了y值 时间戳的那个值 进行对比,如果相等就代表浏览器通过了。
签名Cookie的本质是:
写cookie过程:
- 将值进行base64加密
- 对除值以外的内容进行签名,哈希算法(无法逆向解析)
- 拼接 签名 + 加密值
读cookie过程:
- 读取 签名 + 加密值
- 对签名进行验证
- base64解密,获取值内容
注:许多API验证机制和安全cookie的实现机制相同。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): login_user = self.get_secure_cookie("login_user", None) if login_user: 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): username = self.get_argument('name') password = self.get_argument('pwd') if username == 'wupeiqi' and password == '123': self.set_secure_cookie('login_user', '武沛齐') self.redirect('/') else: self.render('login.html', **{'status': '用户名或密码错误'}) settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh' } application = tornado.web.Application([ (r"/index", MainHandler), (r"/login", LoginHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start() 基于Cookie实现用户验证-Demo
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): return self.get_secure_cookie("login_user") class MainHandler(BaseHandler): @tornado.web.authenticated def get(self): login_user = self.current_user self.write(login_user) class LoginHandler(tornado.web.RequestHandler): def get(self): self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name') password = self.get_argument('pwd') if username == 'wupeiqi' and password == '123': self.set_secure_cookie('login_user', '武沛齐') self.redirect('/') else: self.render('login.html', **{'status': '用户名或密码错误'}) settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh', 'login_url': '/login' } application = tornado.web.Application([ (r"/index", MainHandler), (r"/login", LoginHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start() 基于签名Cookie实现用户验证-Demo
1.2 设置cookie方式
1.2.1 服务端写入到浏览器
1 服务端写入到浏览器 self.set_cookie 常用的 self.cookies #查看所有cookie slef。set_cookie(key,value,expire=day,domain=/) 影响的路径 过期时间 self。get_cookie 获取cookie
1.2.2 通过js 或者jquery写入 按天和按月 ,但是js可以在浏览器禁用 。cookie可以禁用
需要先在网上下载 jQuery。cookie。js 这个文件
<script src="/statics/jquery.cookie.js"></script> <script src="/statics/jquery-1.12.4.js"></script> <script> // javascripts方式 在浏览器设置cookie function setCookieBySeconds(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getSeconds() + expires); document.cookie = name + "= " +value +";expires=" + current_date.toUTCString() }; function setCookieByDays(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getDay() + expires); document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间 } // 调用 // setCookieBySeconds('k1','v1',5) // 5秒过期 // setCookieByDays('k1','v1',5) // 5天过期 //###################jQuery设置################### $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期 //如果不是按天的话,需要先定义时间秒变量 var current_date = new Date(); current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s $.cookie('k1',"v1",{expires:current_date}) //6s 后过期 </scrips>
对于参数:
- domain 指定域名下的cookie
- path 域名下指定url中的cookie
- secure https使用
注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里
1.2.3 cookie 保存周期
cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这 个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。 会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie 保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏 览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式
1.2.4 初始cookie,设置cookie的两种方式 后端写入和前端js jQuery设置项目代码
刚开始的cookie未签名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这 个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。 会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie 保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏 览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式 COOKIE: cookie是存放在浏览器的键值对。 可以用来做用户验证,但是保存在浏览器的信息太多,所以后面很多用户信息放在服务端,浏览器是一个 随机字符串加作为验证信息。 cookie验证机制 服务端 设置cookie验证的时候 (比较靠谱的验证,而不用担心被人一眼看到浏览器觉得cookie就能请求) 1传入值x在tornado内部用base64加密加密后的值比如说是y, 即 y = base64(x) 比如说将这个y作为要发送给浏览器的cookie值得一部分,然后用|分割。 2 时间戳也作为要写入浏览器的cookie值得一部分,然后用|分割。 3 然后将 y值 时间戳, 还有自定义秘钥这三部分进行了单向加密 md5 或者hmac 作为要写入浏览器的cookie值得一部分,然后用|分割。 如上三部分 用|分割作为写入浏览器的cookie的value 浏览器再次请求的时候就会带着这个cookie信息来验证, 服务端会将y值根据base64解密成一个值跟自己的x进行对比,然后,根据服务端自己的y值,自己的时间戳 自己的自定义秘钥这三个再次单向加密md5或者hmac 与浏览器传过来的cookie的除了y值 时间戳的那个值 进行对比,如果相等就代表浏览器通过了。 设置cookie有两种: 1 服务端写入到浏览器 self.set_cookie 常用的 self.cookies #查看所有cookie slef。set_cookie(key,value,expire=day,domain=/) 影响的路径 过期时间 self。get_cookie 获取cookie 2 通过js 或者jquery写入 ,但是js可以在浏览器禁用 。cookie可以禁用 <script src="/statics/jquery.cookie.js"></script> <script src="/statics/jquery-1.12.4.js"></script> <script> // javascripts方式 在浏览器设置cookie function setCookieBySeconds(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getSeconds() + expires); document.cookie = name + "= " +value +";expires=" + current_date.toUTCString() }; function setCookieByDays(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getDay() + expires); document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间 } // 调用 // setCookieBySeconds('k1','v1',5) // 5秒过期 // setCookieByDays('k1','v1',5) // 5天过期 //###################jQuery设置################### $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期 //如果不是按天的话,需要先定义时间秒变量 var current_date = new Date(); current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s $.cookie('k1',"v1",{expires:current_date}) //6s 后过期 </scrips> 对于参数: domain 指定域名下的cookie path 域名下指定url中的cookie secure https使用 注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里 """ class IndexHandler(tornado.web.RequestHandler): def get(self): print(self.cookies) # self.set_cookie("k1","999",) #cookie 的value 是string self.set_secure_cookie("k1","999",) #cookie 的value 是string # print(self.get_cookie("k1")) print(self.get_secure_cookie("k1")) # 获取的是 byte字节类型 需要进行str格式化再比较 self.render("index.html") settings = { "template_path":"views", "static_path":"statics", "static_url_prefix":"/statics/", ### "cookie_secret":"dsfsfsafsdfasdf", } application = tornado.web.Application([ (r"/index",IndexHandler), ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <h1> cookie有两种写入方式: <br> 1 服务端写入到浏览器 <br> 2 浏览器自己通过js写入 document.cookie </h1> </div> <script src="/statics/jquery.cookie.js"></script> <script src="/statics/jquery-1.12.4.js"></script> <script> // javascripts方式 在浏览器设置cookie function setCookieBySeconds(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getSeconds() + expires); document.cookie = name + "= " +value +";expires=" + current_date.toUTCString() }; function setCookieByDays(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getDay() + expires); document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间 } // 调用 // setCookieBySeconds('k1','v1',5) // 5秒过期 // setCookieByDays('k1','v1',5) // 5天过期 //###################jQuery设置################### $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期 //如果不是按天的话,需要先定义时间秒变量 var current_date = new Date(); current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s $.cookie('k1',"v1",{expires:current_date}) //6s 后过期 </script> </body> </html>
初步签名的项目代码
前端的index基本相同
只贴app.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ 较 前面我们可以用cookie 在客户端,我们在服务端封装很多用户信息。 """ container = { } # 封装用户信息的放在程序的全局变量中,如果放在局部变量,当一个用户访问完后,程序的内部变量会销毁。 # 有个弊端 ,1个用户每次请求这个页面的时候都会重新生成随机字符串的cookie值。这样不好。 class IndexHandler(tornado.web.RequestHandler): def get(self): if self.get_argument("u",None) in ["alex","eric"]: import hashlib import time obj = hashlib.md5() obj.update(bytes(str(time.time()),encoding="utf-8")) random_str = obj.hexdigest() container[random_str] = {} #以随机字符串为key #我希望以后以 is_login来判断是否已经登录 container[random_str]["is_login"] = True container[random_str]['k1'] = self.get_argument("u",None)+ "parents" container[random_str]["k2"] = 123 self.set_cookie("__session__",random_str) # 将随机字符串作为key传给客户端 内部会执行之前说过的cookie加密过程 self.write("cookie设置成功了") else: self.write("请登录") class ManaggerHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): print(self.get_cookie("__session__")) random_str = self.get_cookie("__session__") current_user_info = container.get(random_str,None) #get 获取字典的key 没有则返回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["k2"]) # 获取k1的值,获取不到给个空,如果为none字符串格式化的时候回报错 self.write(temp) else: self.redirect("/index") settings = { "template_path":"views", "static_path":"statics", "static_url_prefix":"/statics/", ### "cookie_secret":"dsfsfsafsdfasdf", } application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManaggerHandler), ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
1.2.5 COOKIE 签名 做成一套session机制
上面已经介绍过cookie session了介绍
session是用户信息,可以放在内存,数据库 memcahe redis ,是依赖cookie。是一套机制。不是具体数据。
cookie保存单一键值对,如果需要保存其他内容,则需要写多个cookie,而每次请求的话都会发送所有的cookie这样的话,会造成网络拥堵,此外,cookies是保存在浏览器端,如果保存用户名密码的cookie也放在浏览器端的话,也不够安全,这样就引出了session,session是人为生成的。session也是基于cookie来做的,但是只在浏览器端生成一个cookie(随机字符串,sessionId,token),而在服务端也保存着这段cookie,此外服务端还根据这段cookie可以生成一个字典,字典里面就可以放置用户的其他信息,服务端将这段cookie(sessionId)写到浏览器端,以后浏览器端来访问的时候,服务端根据sessionId从其相应的字典里面获取相应的信息来做相应用户认证机制。session可以保存在全局变量里,放在数据库,文件里面,memcached radis但是不能放在局部变量里。
cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这 个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。 会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie 保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏 览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式 session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。 cookie 和session 的区别: 1、cookie数据存放在客户的浏览器上,session数据放在服务器上。 2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 考虑到安全应当使用session。 3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用COOKIE。 4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。 5、所以个人建议: 将登陆信息等重要信息存放为SESSION 其他信息如果需要保留,可以放在COOKIE中
1.2.6 自定义组件1(后面详细讲) session机制的cookie签名组件
同理上面已经介绍过了index.html自行复制即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这 个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。 会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie 保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏 览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式 session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。 cookie 和session 的区别: 1、cookie数据存放在客户的浏览器上,session数据放在服务器上。 2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 考虑到安全应当使用session。 3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用COOKIE。 4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。 5、所以个人建议: 将登陆信息等重要信息存放为SESSION 其他信息如果需要保留,可以放在COOKIE中 """ container = { } # 封装用户信息的放在程序的全局变量中,如果放在局部变量,当一个用户访问完后,程序的内部变量会销毁。 # 有个弊端 ,1个用户每次请求这个页面的时候都会重新生成随机字符串的cookie值。这样不好。 class Session(tornado.web.RequestHandler): def __init__(self,handler): self.handler = handler def __generate_random_str(self): import hashlib import time obj = hashlib.md5() obj.update(bytes(str(time.time()), encoding="utf-8")) random_str = obj.hexdigest() return random_str def set_value(self,key,vlaue): #在contain定义随机字符串, # 定义自己的数据 #在浏览器写入随机字符串 random_str = self.__generate_random_str() container[random_str] = {} # 以随机字符串为key # 我希望以后以 is_login来判断是否已经登录 container[random_str][key] = vlaue self.handler.set_cookie("__session__",random_str) def get_value(self,key): random_str = self.handler.get_cookie("__session__",None) print(random_str) user_info_dict = container.get(random_str,None) # 获取不到返回空 if user_info_dict: value = user_info_dict[key] return value # 有个弊端 ,1个用户每次请求这个页面的时候都会重新生成随机字符串的cookie值。这样不好。 class IndexHandler(tornado.web.RequestHandler): def get(self): if self.get_argument("u",None) in ["alex","eric"]: s = Session(self) #将 Indexhandler 类实例化对象传入 s.set_value("is_login",True) else: self.write("请登录") class ManaggerHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): print(container) # print(self.get_cookie("__session__")) s = Session(self) val = s.get_value("is_login") if val: self.write("登录成功") else: self.write("登录失败") settings = { "template_path":"views", "static_path":"statics", "static_url_prefix":"/statics/", ## "cookie_secret":"dsfsfsafsdfasdf", } application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManaggerHandler), ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
加强完整验证版 app.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ session 1、面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法 obj = class1() obj() ====对象加括号调用的是类的__call__方法 如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] #obj['k2'] = 'wupeiqi' #del obj['k1'] """ container = {} # container = { # # "第一个人的随机字符串":{}, # # "第一个人的随机字符串":{'k1': 111, 'parents': '你'}, # } class Session: def __init__(self, handler): self.handler = handler self.random_str = None def __genarate_random_str(self): import hashlib import time 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中加入随机字符串 # 定义专属于自己的数据 # 在客户端中写入随机字符串 # 判断,请求的用户是否已有随机字符串 if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了 random_str = self.handler.get_cookie('__session__') 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 # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value self.handler.set_cookie("__session__", self.random_str) def get_value(self,key): # 获取客户端的随机字符串 # 从container中获取专属于我的数据 # 专属信息【key】 random_str = self.handler.get_cookie("__session__") 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) return value class IndexHandler(tornado.web.RequestHandler): def get(self): if self.get_argument("u",None) in ["alex","eric"]: s = Session(self) #将 Indexhandler 类实例化对象传入 s.set_value("is_login",True) s.set_value("name",self.get_argument("u",None)) print(container) self.write("cookie设置成功了") else: self.write("请登录") class ManaggerHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): print(container) # print(self.get_cookie("__session__")) s = Session(self) val = s.get_value("is_login") if val: self.write(s.get_value("name")+"登录成功") else: self.write("登录失败121212") settings = { "template_path":"views", "static_path":"statics", "static_url_prefix":"/statics/", "cookie_secret":"dsfsfsafsdfasdf", } application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManaggerHandler), ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
改成面向对象的 session类调用cookie设置值
完整app
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ session 1、面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法 obj = class1() obj() ====对象加括号调用的是类的__call__方法 如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] #obj['k2'] = 'wupeiqi' #del obj['k1'] """ container = {} # container = { # # "第一个人的随机字符串":{}, # # "第一个人的随机字符串":{'k1': 111, 'parents': '你'}, # } class Session: def __init__(self, handler): self.handler = handler self.random_str = None def __genarate_random_str(self): import hashlib import time 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): def __setitem__(self, key, value): # 在container中加入随机字符串 # 定义专属于自己的数据 # 在客户端中写入随机字符串 # 判断,请求的用户是否已有随机字符串 if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了 random_str = self.handler.get_cookie('__session__') 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 # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value self.handler.set_cookie("__session__", self.random_str) def __getitem__(self, key): # 获取客户端的随机字符串 # 从container中获取专属于我的数据 # 专属信息【key】 random_str = self.handler.get_cookie("__session__") print("client ", random_str) 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) return value class IndexHandler(tornado.web.RequestHandler): def get(self): if self.get_argument("u",None) in ["alex","eric"]: s = Session(self) #将 Indexhandler 类实例化对象传入 s["is_login"] = True s["name"] = self.get_argument("u",None) print(container) self.write("dffff df cookie设置成功了") else: self.write("请d d s 登录") class ManaggerHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): print(container) # print(self.get_cookie("__session__")) s = Session(self) val = s["is_login"] if val: self.write(s["name"]+"登录成功") else: self.write("登录失败1sdfdsfds2") settings = { "template_path":"views", "static_path":"statics", "static_url_prefix":"/statics/", "cookie_secret":"dsfsfsafsdfasdf", } application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManaggerHandler), ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
需要了解
1、面向对象基础
面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法
obj = class1()
obj() ====对象加括号调用的是类的__call__方法
#!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] #obj['k2'] = 'wupeiqi' #del obj['k1']
2、Tornado扩展
Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作...
class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.xxoo = "wupeiqi" class MainHandler(BaseHandler): def get(self): print(self.xxoo) self.write('index') class IndexHandler(BaseHandler): def get(self): print(self.xxoo) self.write('index')
3、session
session其实就是定义在服务器端用于保存用户回话的容器,其必须依赖cookie才能实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from hashlib import sha1 import os, time session_container = {} create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest() class Session(object): session_id = "__sessionId__" def __init__(self, request): session_value = request.get_cookie(Session.session_id) if not session_value: self._id = create_session_id() else: self._id = session_value request.set_cookie(Session.session_id, self._id) def __getitem__(self, key): return session_container[self._id][key] def __setitem__(self, key, value): if session_container.has_key(self._id): session_container[self._id][key] = value else: session_container[self._id] = {key: value} def __delitem__(self, key): del session_container[self._id][key] class BaseHandler(tornado.web.RequestHandler): def initialize(self): # my_session['k1']访问 __getitem__ 方法 self.my_session = Session(self) class MainHandler(BaseHandler): def get(self): print self.my_session['c_user'] print self.my_session['c_card'] self.write('index') class LoginHandler(BaseHandler): def get(self): self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name') password = self.get_argument('pwd') if username == 'wupeiqi' and password == '123': self.my_session['c_user'] = 'wupeiqi' self.my_session['c_card'] = '12312312309823012' self.redirect('/index') else: self.render('login.html', **{'status': '用户名或密码错误'}) settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh', 'login_url': '/login' } application = tornado.web.Application([ (r"/index", MainHandler), (r"/login", LoginHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
2 随机验证码图片
验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。
安装图像处理模块:
pip3 install pillow
需要一个字体文件 Monaco.ttf
登陆注册的时候,需要验证码的功能,原理为在后台自动创建一张随机图片,然后通过img标签输出到前端。这里我们需要安装一个pillow的模块,相应的生成随机验证代码文件如下,此外还需要一个字体文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #coding:utf-8 import random from PIL import Image, ImageDraw, ImageFont, ImageFilter _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z _upper_cases = _letter_cases.upper() # 大写字母 _numbers = ''.join(map(str, range(3, 10))) # 数字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) # 路径拼接 # import os # path = os.path.join(os.path.dirname(__file__),"Monaco.ttf") def create_validate_code(size=(120, 30), chars=init_chars, img_type="GIF", mode="RGB", bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, font_type="Monaco.ttf", # font_type=path, length=4, draw_lines=True, n_line=(1, 2), draw_points=True, point_chance = 2): ''' @todo: 生成验证码图片 @param size: 图片的大小,格式(宽,高),默认为(120, 30) @param chars: 允许的字符集合,格式字符串 @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG @param mode: 图片模式,默认为RGB @param bg_color: 背景颜色,默认为白色 @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF @param font_size: 验证码字体大小 @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf @param length: 验证码字符个数 @param draw_lines: 是否划干扰线 @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效 @param draw_points: 是否画干扰点 @param point_chance: 干扰点出现的概率,大小范围[0, 100] @return: [0]: PIL Image实例 @return: [1]: 验证码图片中的字符串 ''' width, height = size # 宽, 高 img = Image.new(mode, size, bg_color) # 创建图形 draw = ImageDraw.Draw(img) # 创建画笔 def get_chars(): '''生成给定长度的字符串,返回列表格式''' return random.sample(chars, length) def create_lines(): '''绘制干扰线''' line_num = random.randint(*n_line) # 干扰线条数 for i in range(line_num): # 起始点 begin = (random.randint(0, size[0]), random.randint(0, size[1])) #结束点 end = (random.randint(0, size[0]), random.randint(0, size[1])) draw.line([begin, end], fill=(0, 0, 0)) def create_points(): '''绘制干扰点''' chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width): for h in range(height): tmp = random.randint(0, 100) if tmp > 100 - chance: draw.point((w, h), fill=(0, 0, 0)) def create_strs(): '''绘制验证码字符''' c_chars = get_chars() strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 font = ImageFont.truetype(font_type, font_size) font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines: create_lines() if draw_points: create_points() strs = create_strs() # 图形扭曲参数 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, strs
注意 check_code文件内的字体MONACO.TTF文件路径在tornado访问注意路径拼接访问
如下这种格式
# 路径拼接 # import os # path = os.path.join(os.path.dirname(__file__),"Monaco.ttf") def create_validate_code(size=(120, 30), 。。。。。 font_type=path, 。。。。
首先在登录界面的 验证码的img标签的src配置一个路由uri 即 <img src="/check_code"> 定义了一个事件,点击时候,获取图片src 并个这个标签的src加问号,让图片的src刷新,相当于发送get请求/check_code url这样
后端的的handler对应的checkcode的类get方法就重新生成img二进制数据self.write给这个uri
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login" method="post"> <p><input name="user" type="text" placeholder="用户"/></p> <p><input name='pwd' type="text" placeholder="密码"/></p> <p> <input name="code" type="text" placeholder="验证码"/> <img src="/check_code" onclick='ChangeCode();' id='imgCode'> </p> <input type="submit" value="Submit" /> <span style="color:red">{{status}}</span> </form> <script type="text/javascript"> function ChangeCode() { var code = document.getElementById('imgCode'); code.src += '?'; } </script> </body> </html>
<script type="text/javascript"> function ChangeCode() { var code = document.getElementById('imgCode'); code.src += '?'; } </script>
index.html 跟上面一样,这里贴一下吧
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <h1> cookie有两种写入方式: <br> 1 服务端写入到浏览器 <br> 2 浏览器自己通过js写入 document.cookie </h1> </div> <script src="/statics/jquery.cookie.js"></script> <script src="/statics/jquery-1.12.4.js"></script> <script> // javascripts方式 在浏览器设置cookie function setCookieBySeconds(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getSeconds() + expires); document.cookie = name + "= " +value +";expires=" + current_date.toUTCString() }; function setCookieByDays(name,value,expires) { var current_date = new Date(); current_date.setDate(current_date.getDay() + expires); document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间 } // 调用 // setCookieBySeconds('k1','v1',5) // 5秒过期 // setCookieByDays('k1','v1',5) // 5天过期 //###################jQuery设置################### $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期 //如果不是按天的话,需要先定义时间秒变量 var current_date = new Date(); current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s $.cookie('k1',"v1",{expires:current_date}) //6s 后过期 </script> </body> </html>
接下来是后端的处理程序了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 图片验证码 class CheckCodeHandler(BaseHandler): def get(self, *args, **kwargs): #生成 图片并且返回 import io import check_code mstream = io.BytesIO() img, code = check_code.create_validate_code() # 将图片对象写入mstream, img.save(mstream,"GIF") # 为每个用户保存期验证码 self.session["CheckCode"] = code self.write(mstream.getvalue())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ session 1、面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法 obj = class1() obj() ====对象加括号调用的是类的__call__方法 如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] #obj['k2'] = 'wupeiqi' #del obj['k1'] """ container = {} # container = { # # "第一个人的随机字符串":{}, # # "第一个人的随机字符串":{'k1': 111, 'parents': '你'}, # } class Session: def __init__(self, handler): self.handler = handler self.random_str = None def __genarate_random_str(self): import hashlib import time 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): def __setitem__(self, key, value): # 在container中加入随机字符串 # 定义专属于自己的数据 # 在客户端中写入随机字符串 # 判断,请求的用户是否已有随机字符串 if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了 random_str = self.handler.get_cookie('__session__') 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 # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value self.handler.set_cookie("__session__", self.random_str) def __getitem__(self, key): # 获取客户端的随机字符串 # 从container中获取专属于我的数据 # 专属信息【key】 random_str = self.handler.get_cookie("__session__") print("client ", random_str) 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) return value class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = Session(self)#将 Indexhandler 类实例化对象传入 class IndexHandler(BaseHandler): def get(self): if self.get_argument("u",None) in ["alex","eric"]: self.session["is_login"] = True self.session["name"] = self.get_argument("u",None) print(container) self.write("dffff df cookie设置成功了") else: self.write("请d d s 登录") class ManaggerHandler(BaseHandler): def get(self, *args, **kwargs): print(container) # print(self.get_cookie("__session__")) val = self.session["is_login"] if val: self.write(self.session["name"]+"登录成功") else: self.write("登录失败1sdfdsfds2") # 图片验证码 class CheckCodeHandler(BaseHandler): def get(self, *args, **kwargs): #生成 图片并且返回 import io import check_code mstream = io.BytesIO() img, code = check_code.create_validate_code() # 将图片对象写入mstream, img.save(mstream,"GIF") # 为每个用户保存期验证码 self.session["CheckCode"] = code self.write(mstream.getvalue()) class LoginHandler(BaseHandler): def get(self, *args, **kwargs): 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='验证码错误') settings = { "template_path":"views", "static_path":"statics", "static_url_prefix":"statics", "cookie_secret":"dsfsfsafsdfasdf", } application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManaggerHandler), (r"/login", LoginHandler), (r"/check_code", CheckCodeHandler), ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
3 xss跨站脚本攻击 csrf跨站请求伪造
Xss跨站脚本攻击
csrf跨站请求伪造
get请求的时候,会给浏览器发一个id(cookie),浏览器post请求的时候,携带这个id,然后服务端对其做验证,如果没有这个id的话,就禁止浏览器提交内容。下面来看一下在tornado里面怎么设置,首先需要在settings里面配置 'xsrf_cookies': True,如果这样配置的话,浏览器发送post请求的话这样设置之后,Tornado 将拒绝请求参数中不包含正确的_xsrf
值的 post/put/delete 请求,如果没有携带相应的id(session)则会禁止访问。{% raw xsrf_form_html() %}
是新增的,目的就在于实现上面所说的授权给前端以合法请求。
这里主要看下html里面的代码,用js获取_xsrf对应的session,然后用jquery的ajax发送post请求,并且将_xsrf的session也发送过去。
Tornado中的夸张请求伪造和Django中的相似,跨站伪造请求(Cross-site request forgery)
1 后端配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
settings = { "xsrf_cookies": True, } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<form action="/new_message" method="post"> {% raw xsrf_form_html() %} <input type="text" name="message"/> <input type="submit" value="Post"/> </form>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 + ")")); }}); }; 使用 - AJAX
index.html login.html 不变 跟上面的一样
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/csrf" method="post"> {% raw xsrf_form_html() %} <!--输出结果如下--> <!--<input type="hidden" name="_xsrf" value="2|1a3c1c71|31ad9ca267d4350e09d426eb66244e5b|1470061982">--> <p><input name="user" type="text" placeholder="用户"/></p> <p><input name='pwd' type="text" placeholder="密码"/></p> <p> <input name="code" type="text" placeholder="验证码"/> <img src="/check_code" onclick='ChangeCode();' id='imgCode'> </p> <input type="submit" value="Submit" /> <span style="color:red"></span> </form> <script src="/statics/jquery-1.12.4.js"></script> <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" /> <script type="text/javascript"> function ChangeCode() { var code = document.getElementById('imgCode'); code.src += '?'; } function getCookie(name) { var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); return r ? r[1] : undefined; /*document.cookie "k1=2|1:0|10:1469588097|2:k1|4:OTk5|1b6ea1b4be4fa44f24e1add4447c74c27f969248bef14f19b347ed9f703b20a5; user="2|1:0|10:1469606426|4:user|8:YWxleA==|465b3e5dc3a2585bd5f5200a46c11e406b759a4c4fd7d96f5554b99bcdc619e9"; _xsrf=2|53738789|78e2075a2e9baef6409bbd132f6bd5a3|1470061982; __session__=f01a331979313ee036accf4be458ef11" name = "_xsrf" "_xsrf" document.cookie.match("\\b" + name + "=([^;]*)\\b"); ["_xsrf=2|53738789|78e2075a2e9baef6409bbd132f6bd5a3|1470061982", "2|53738789|78e2075a2e9baef6409bbd132f6bd5a3|1470061982"]*/ } function SubmitCsrf() { var nid = getCookie('_xsrf'); //csrf的name名 $.post({ url: '/csrf', data: {'k1': 'v1',"_xsrf": nid}, success: function (callback) { // Ajax请求发送成功有,自动执行 // callback,服务器write的数据 callback=“csrf.post” console.log(callback); } }); } </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ import tornado.web import tornado.ioloop """ session 1、面向对象基础 面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法 obj = class1() obj() ====对象加括号调用的是类的__call__方法 如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key): print '__getitem__',key def __setitem__(self, key, value): print '__setitem__',key,value def __delitem__(self, key): print '__delitem__',key obj = Foo() result = obj['k1'] #obj['k2'] = 'wupeiqi' #del obj['k1'] """ container = {} # container = { # # "第一个人的随机字符串":{}, # # "第一个人的随机字符串":{'k1': 111, 'parents': '你'}, # } class Session: def __init__(self, handler): self.handler = handler self.random_str = None def __genarate_random_str(self): import hashlib import time 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): def __setitem__(self, key, value): # 在container中加入随机字符串 # 定义专属于自己的数据 # 在客户端中写入随机字符串 # 判断,请求的用户是否已有随机字符串 if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了 random_str = self.handler.get_cookie('__session__') 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 # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value self.handler.set_cookie("__session__", self.random_str) def __getitem__(self, key): # 获取客户端的随机字符串 # 从container中获取专属于我的数据 # 专属信息【key】 random_str = self.handler.get_cookie("__session__") print("client ", random_str) 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) return value class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = Session(self)#将 Indexhandler 类实例化对象传入 class IndexHandler(BaseHandler): def get(self): if self.get_argument("u",None) in ["alex","eric"]: self.session["is_login"] = True self.session["name"] = self.get_argument("u",None) print(container) self.write("dffff df cookie设置成功了") else: self.write("请d d s 登录") class ManaggerHandler(BaseHandler): def get(self, *args, **kwargs): print(container) # print(self.get_cookie("__session__")) val = self.session["is_login"] if val: self.write(self.session["name"]+"登录成功") else: self.write("登录失败1sdfdsfds2") # 图片验证码 class CheckCodeHandler(BaseHandler): def get(self, *args, **kwargs): #生成 图片并且返回 import io import check_code mstream = io.BytesIO() img, code = check_code.create_validate_code() # 将图片对象写入mstream, img.save(mstream,"GIF") # 为每个用户保存期验证码 self.session["CheckCode"] = code self.write(mstream.getvalue()) class LoginHandler(BaseHandler): def get(self, *args, **kwargs): 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='验证码错误') # csrf 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":"statics", "static_url_prefix":"/statics/", "cookie_secret":"dsfsfsafsdfasdf", 'xsrf_cookies': True # csrf的cookie } application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManaggerHandler), (r"/login", LoginHandler), (r"/check_code", CheckCodeHandler), (r"/csrf", CsrfHandler), # 定义路由 ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
注:Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求
文件上传的方式
1、Form表单上传
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>上传文件</title> </head> <body> <form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" > <input name="fff" id="my_file" type="file" /> <input type="submit" value="提交" /> </form> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') def post(self, *args, **kwargs): file_metas = self.request.files["fff"] # print(file_metas) for meta in file_metas: file_name = meta['filename'] with open(file_name,'wb') as up: up.write(meta['body']) settings = { 'template_path': 'template', } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8000) tornado.ioloop.IOLoop.instance().start()
2、AJAX上传
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <input type="file" id="img" /> <input type="button" onclick="UploadFile();" /> <script> function UploadFile(){ var fileObj = document.getElementById("img").files[0]; var form = new FormData(); form.append("k1", "v1"); form.append("fff", fileObj); var xhr = new XMLHttpRequest(); xhr.open("post", '/index', true); xhr.send(form); } </script> </body> </html> HTML - XMLHttpRequest
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <input type="file" id="img" /> <input type="button" onclick="UploadFile();" /> <script> function UploadFile(){ var fileObj = $("#img")[0].files[0]; var form = new FormData(); form.append("k1", "v1"); form.append("fff", fileObj); $.ajax({ type:'POST', url: '/index', data: form, processData: false, // tell jQuery not to process the data contentType: false, // tell jQuery not to set contentType success: function(arg){ console.log(arg); } }) } </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" > <div id="main"> <input name="fff" id="my_file" type="file" /> <input type="button" name="action" value="Upload" onclick="redirect()"/> <iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe> </div> </form> <script> function redirect(){ document.getElementById('my_iframe').onload = Testt; document.getElementById('my_form').target = 'my_iframe'; document.getElementById('my_form').submit(); } function Testt(ths){ var t = $("#my_iframe").contents().find("body").text(); console.log(t); } </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') def post(self, *args, **kwargs): file_metas = self.request.files["fff"] # print(file_metas) for meta in file_metas: file_name = meta['filename'] with open(file_name,'wb') as up: up.write(meta['body']) settings = { 'template_path': 'template', } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8000) tornado.ioloop.IOLoop.instance().start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<script type="text/javascript"> $(document).ready(function () { $("#formsubmit").click(function () { var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>'); $("body").append(iframe); var form = $('#theuploadform'); form.attr("action", "/upload.aspx"); form.attr("method", "post"); form.attr("encoding", "multipart/form-data"); form.attr("enctype", "multipart/form-data"); form.attr("target", "postiframe"); form.attr("file", $('#userfile').val()); form.submit(); $("#postiframe").load(function () { iframeContents = this.contentWindow.document.body.innerHTML; $("#textarea").html(iframeContents); }); return false; }); }); </script> <form id="theuploadform"> <input id="userfile" name="userfile" size="50" type="file" /> <input id="formsubmit" type="submit" value="Send File" /> </form> <div id="textarea"> </div>