RequestHandler提供了操作cookie的方法
set_cookie(name, value, domain=None, expires=None, path='/', expires_days=None)
参数名 | 说明 |
---|---|
name | cookie名 |
value | cookie值 |
domain | 提交cookie时匹配的域名 |
path | 提交cookie时匹配的路径 |
expires | cookie的有效期,可以是时间戳整数、时间元组或者datetime类型,为UTC时间 |
expires_days | cookie的有效期,天数,优先级低于expires |
import tornado.web
import tornado.ioloop
import time
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.set_cookie("cookie1", "version1")
self.set_cookie("cookie2", "version2", path="/cookie2",
expires=time.strptime("2019-08-11 23:59:59", "%Y-%m-%d %H:%M:%S"))
self.set_cookie("cookie3", "version3", expires_days=20)
# 利用time.mktime将本地时间转换为UTC标准时间
self.set_cookie("cookie4", "version4",
expires=time.mktime(time.strptime("2019-08-11 23:59:59", "%Y-%m-%d %H:%M:%S")))
self.write("OK")
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', MainHandler), # 生成服务实例,通常包含路由信息
], debug=True)
app.listen(8000) # 监听端口
tornado.ioloop.IOLoop.current().start() # 开启服务
设置cookie实际是通过设置header的Set-Cookie来实现
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.set_cookie("cookie1", "version1")
self.set_cookie("cookie2", "version2", path="/cookie2",
expires=time.strptime("2019-08-11 23:59:59", "%Y-%m-%d %H:%M:%S"))
self.set_cookie("cookie3", "version3", expires_days=20)
# 利用time.mktime将本地时间转换为UTC标准时间
self.set_cookie("cookie4", "version4",
expires=time.mktime(time.strptime("2019-08-11 23:59:59", "%Y-%m-%d %H:%M:%S")))
# 通过set_header来设置cookie
self.set_header("Set-Cookie", "cookie5=version5; expires=Fri, 11 Aug 2019 15:59:59 GMT; Path=/")
self.write("OK")
get_cookie(name, default=None)
import tornado.web
import tornado.ioloop
import tornado.httpserver
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.set_cookie("cookie1", "version1") # 设置cookie
cookie = self.get_cookie("cookie1") # 获取cookie
self.write(cookie) # 将cookie打印出来
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', MainHandler)
], debug=True)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(8000)
tornado.ioloop.IOLoop.current().start()
clear_cookie(name, path='/', domain=None)
删除名为name,并同时匹配domain和path的cookie
clear_all_cookies(path='/', domain=None)
删除同时匹配domain和path的所有cookie
import tornado.web
import tornado.ioloop
import time
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.set_cookie("cookie1", "version1")
self.set_cookie("cookie2", "version2", path="/cookie2",
expires=time.strptime("2019-08-11 23:59:59", "%Y-%m-%d %H:%M:%S"))
self.set_cookie("cookie3", "version3", expires_days=20)
# 利用time.mktime将本地时间转换为UTC标准时间
self.set_cookie("cookie4", "version4",
expires=time.mktime(time.strptime("2019-08-11 23:59:59", "%Y-%m-%d %H:%M:%S")))
self.write("OK")
class ClearOneCookieHandler(tornado.web.RequestHandler):
def get(self):
self.clear_cookie("cookie3") # 清除cookie3对应的cookie值
self.write("OK")
class ClearAllCookieHandler(tornado.web.RequestHandler):
def get(self):
self.clear_all_cookies() # 清除所有cookie
self.write("OK")
class CookieHandler(tornado.web.RequestHandler):
def get(self):
self.write("OK")
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', MainHandler), # 生成服务实例,通常包含路由信息
(r'/clear_one', ClearOneCookieHandler),
(r'/clear_all', ClearAllCookieHandler),
(r'/cookie2', CookieHandler)
], debug=True)
app.listen(8000) # 监听端口
tornado.ioloop.IOLoop.current().start() # 开启服务
执行清除cookie操作后,并不是立即删除了浏览器中的cookie,而是给cookie值置空,并改变其有效期使其失效。真正的删除cookie是由浏览器去清理的
Tornado提供了一种对Cookie进行简易加密签名的方法来防止Cookie被恶意篡改
安全Cookie需要为应用配置一个用来给Cookie进行签名的秘钥cookie_secret
,将其传递给Application
的构造函数
uuid模块的
uuid4()
函数可以随机产生一个uuid
码,bytes
属性将此uuid
码作为16字节字符串
Base64是一种基于64个可打印字符来表示二进制数据的表示方法
两者结合使用可以生成Cookie签名需要的secret key
生成secret_key
import base64
import uuid
print(base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes))
# hhLgDUloTO2hKpawAGathnZEwNDbDEAOrNZQLj1DAzk=
app = tornado.web.Application(
[(r"/", MainHandler),],
cookie_secret = "hhLgDUloTO2hKpawAGathnZEwNDbDEAOrNZQLj1DAzk="
)
set_secure_cookie(name, value, expires_days=30)
设置一个带签名和时间戳的cookie,防止cookie被伪造
get_secure_cookie(name, value=None, max_age_days=31)
如果cookie存在且验证通过,返回cookie的值,否则返回None
expires_days是设置浏览器中cookie的有效期,而max_age_day是过滤安全cookie的时间戳
import tornado.web
import tornado.ioloop
class MainHandler(tornado.web.RequestHandler):
def get(self):
cookie = self.get_secure_cookie("count")
count = int(cookie) + 1 if cookie else 1
self.set_secure_cookie("count", str(count))
self.write(
'Cookie Counter '
'访问量:%d次。
' % count +
''
)
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', MainHandler),
],
debug=True,
cookie_secret="hhLgDUloTO2hKpawAGathnZEwNDbDEAOrNZQLj1DAzk="
)
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
签名后的cookie
"2|1:0|10:1563683178|5:count|4:MTU=|bceba900821617648a262cde89b0961257eae5afdbafa19b9fea250b6cb62d55"
字段说明:
Tornado的安全cookie只是一定程度的安全,仅仅是增加了恶意修改的难度
Tornado的安全cookies仍然容易被窃听,而cookie值是签名不是加密,攻击者能够读取已存储的cookie值,并且可以传输他们的数据到任意服务器,或者通过发送没有修改的数据给应用伪造请求
避免在浏览器cookie中存储敏感的用户数据仍然非常重要
浏览器有一个很重要的概念——同源策略(Same-Origin Policy)
。 所谓同源是指,域名,协议,端口相同。 不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源
由于第三方站点没有访问cookie数据的权限(同源策略),所以我们可以要求每个请求包括一个特定的参数值作为令牌来匹配存储在cookie中的对应值,如果两者匹配,我们的应用认定请求有效
import tornado.web
import tornado.ioloop
class MainHandler(tornado.web.RequestHandler):
def get(self): # 向当前url发送post请求
self.write(
"""""")
def post(self):
self.write("hello world")
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', MainHandler),
],
debug=True,
cookie_secret="hhLgDUloTO2hKpawAGathnZEwNDbDEAOrNZQLj1DAzk=",
xsrf_cookies=True # 打开xsrf验证需求并生成cookie
)
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
我们知道实现xsrf
就是在发送post
请求的时候将xsrf_cookie
值一同发送给服务器,让服务器对两个cookie
进行比较来判断请求是否合法
因此可以为模板的表单中添加了一个隐藏的输入名为xsrf
,其值为xsrf
的Cookie
值
当通过此html页面发送post请求时,浏览器会将模板中隐藏的cookie
发送到服务器
import tornado.web
import tornado.ioloop
class MainHandler(tornado.web.RequestHandler):
def get(self): # 向当前url发送post请求
self.render("main.html")
def post(self):
self.write("hello world")
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', MainHandler),
],
debug=True,
cookie_secret="hhLgDUloTO2hKpawAGathnZEwNDbDEAOrNZQLj1DAzk=",
xsrf_cookies=True # 打开xsrf验证需求
)
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
main.html
<html>
<head>
<title>XSRFtitle>
head>
<body>
<form method="post">
{% module xsrf_form_html() %}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
form>
body>
html>
在任意的Handler中通过获取self.xsrf_token的值来生成_xsrf并设置Cookie
import tornado.web
import tornado.ioloop
class XSRFTokenHandler(tornado.web.RequestHandler):
def get(self):
self.render("main2.html") # 加载模板
def post(self):
self.write("OK")
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', XSRFTokenHandler),
],
debug=True,
cookie_secret="hhLgDUloTO2hKpawAGathnZEwNDbDEAOrNZQLj1DAzk=",
xsrf_cookies=True # 打开xsrf验证需求
)
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
main2.html
<html>
<head>
<meta charset="utf-8">
<title>XSRFtitle>
head>
<body>
<a href="javascript:;" onclick="xsrfPost()">发送POST请求-main2a>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js">script>
<script type="text/javascript">
//获取指定Cookie的函数
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
//AJAX发送post请求,表单格式数据
function xsrfPost() {
var xsrf = getCookie("_xsrf");
$.post("/", "_xsrf=" + xsrf, function (data) {
alert("OK");
});
}
script>
body>
html>
请求体是其他格式的(如json或xml等),可以通过设置HTTP头X-XSRFToken来传递_xsrf值
<html>
<head>
<meta charset="utf-8">
<title>测试XSRFtitle>
head>
<body>
<a href="javascript:;" onclick="xsrfPost()">发送POST请求-main3a>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js">script>
<script type="text/javascript">
//获取指定Cookie的函数
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
//AJAX发送post请求,json格式数据
function xsrfPost() {
var xsrf = getCookie("_xsrf");
var data = {}; //请求参数,这里没有设置
var json_data = JSON.stringify(data);
$.ajax({
url: "/", //请求地址
method: "POST", //请求方式
headers: {
"X-XSRFToken": xsrf, //请求头中设置xsrf信息
},
data: json_data,
success: function (data) {
alert("OK");
}
})
}
script>
body>
html>