01cookie
在上节课,我们简单了解了登录过程,但是很明显,每次都需要登录,但是在平常逛网站的只需要登录一次,那么网站是如何记录登录信息的呢?
有没有什么办法可以让浏览器记住登录信息,下次再次打开的时候,可以自动登录呢?
cookie机制
Cookie是由 服务器端 生成,发送给 User-Agent , 浏览器会将 Cookie的 key/value 保存到某个目录下的文本文件内,下次请求同一网站时就发送该 Cookie 给服务器
服务器向客户端发送 cookie
浏览器将 cookie 保存
之后每次 http请求 浏览器都会将 cookie 发送给服务器端
客户端发送一个 http请求 到服务器端
服务器端发送一个 http响应 + Set-Cookie 到客户端
客户端发送一个 http请求 + Cookie 到服务器端
服务器端发送一个 http响应 到客户端
在客户端的第二次请求中包含的Cookie头部中,提供给了服务器端可以用来唯一标识客户端身份的信息
设置 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 |
设置cookie原理
设置cookie实际就是通过设置header的Set-Cookie来实现的。
import datetime
class SetCookiehandler(RequestHandler):
def get(self):
# 默认过期时间是浏览器关闭会话时
self.set_cookie('cookie_test','this_is_test')
# 设置过期时间为60秒
self.set_cookie('cookie_test1','this_is_test',expires=time.time()+60)
# 设置过期时间为一天
self.set_cookie('cookie_test2','this_is_test',expires_days=1)
# 设置路径
self.set_cookie('cookie_test3','this_is_test',path='/')
# 设置 js 不可以获取cookie
self.set_cookie('cookie_test4','this_is_test',httponly=True)
# max_age 优先级高
self.set_cookie('cookie_test5','this_is_test',max_age=120,expires=time.time()+60)
# 设置加密cookie
# 需要设置 application 的 cookie_secret 参数
self.set_secure_cookie('cookie_test6','this_is_cookie')
获取 cookie
get_cookie(name, default=None)
class GetCookieHandler(tornado.web.RequestHandler):
def get(self):
self.write(self.get_cookie('cookie_test'))
self.write('<br>')
self.write(self.get_secure_cookie('cookie_test6'))
this_is_test
this_is_cookie
清除cookie
clear_cookie(name, path=’/’, domain=None)
删除名为name,并同时匹配domain和path的cookie。
clear_all_cookies(path=’/’, domain=None)
删除同时匹配domain和path的所有cookie。
class ClearOneCookieHandler(RequestHandler):
def get(self):
self.clear_cookie("cookie_test")
self.write("OK")
class ClearAllCookieHandler(RequestHandler):
def get(self):
self.clear_all_cookies()
self.write("OK")
注意:执行清除cookie操作后,并不是立即删除了浏览器中的cookie,而是给cookie值置空,并改变其有效期使其失效。真正的删除cookie是由浏览器去清理的。
代码
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
import time
from tornado.options import define, options
define(name='port', default=8080, type=int, help='run port')
class Indexhandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('ddd')
class SetCookiehandler(tornado.web.RequestHandler):
def get(self):
self.set_cookie('cookie_test', 'this_is_test')
self.set_cookie('cookie_test1', 'this_is_test', expires=time.time() + 120)
self.set_cookie('cookie_test2', 'this_is_test', expires_days=1)
self.set_cookie('cookie_test3', 'this_is_test', path='/')
self.set_cookie('cookie_test4', 'this_is_test', httponly=True)
self.set_cookie('cookie_test5', 'this_is_test', max_age=120, expires=time.time() + 60)
self.set_secure_cookie('cookie_test6', 'this_is_cookie')
self.write('cookie set')
class GetCookieHandler(tornado.web.RequestHandler):
def get(self):
self.write(self.get_cookie('cookie_test'))
self.write('<br>')
self.write(self.get_secure_cookie('cookie_test6'))
class ClearOneCookieHandler(tornado.web.RequestHandler):
def get(self):
self.clear_cookie("cookie_test")
self.write("OK")
class ClearAllCookieHandler(tornado.web.RequestHandler):
def get(self):
self.clear_all_cookies()
self.write("OK")
application = tornado.web.Application(
handlers=[
(r'/', Indexhandler),
(r'/set', SetCookiehandler),
(r'/get', GetCookieHandler),
(r'/clearone', ClearOneCookieHandler),
(r'/clearall', ClearAllCookieHandler),
],
template_path='template',
cookie_secret = 'R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM=',
debug=True
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
安全Cookie
Cookie是存储在客户端浏览器中的,很容易被篡改。Tornado提供了一种对Cookie进行简易加密签名的方法来防止Cookie被恶意篡改。
使用安全Cookie需要为应用配置一个用来给Cookie进行混淆的秘钥cookie_secret,将其传递给Application的构造函数。我们可以使用如下方法来生成一个随机字符串作为cookie_secret的值。
>>> import base64, uuid
>>> base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
b'R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM='
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。
uuid, 通用唯一识别码(英语:Universally Unique Identifier,简称UUID),是由一组32个16进制数字所构成(两个16进制数是一个字节,总共16字节),因此UUID理论上的总数为16^32=2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
uuid模块的uuid4()函数可以随机产生一个uuid码,bytes属性将此uuid码作为16字节字符串。
将生成的cookie_secret传入Application构造函数:
application = tornado.web.Application(
handlers = [
(r"/", IndexHandler),
],
cookie_secret = "R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM="
)
获取和设置
set_secure_cookie(name, value, expires_days=30)
设置一个带签名和时间戳的cookie,防止cookie被伪造。
get_secure_cookie(name, value=None, max_age_days=31)
如果cookie存在且验证通过,返回cookie的值,否则返回None。max_age_day不同于expires_days,expires_days是设置浏览器中cookie的有效期,而max_age_day是过滤安全cookie的时间戳。
class IndexHandler(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(
'<html>'
'<head><title>Cookie计数器</title></head>'
'<body><h1>您已访问本页%d次。</h1>' % count +
'</body>'
'</html>'
)
我们看签名后的cookie值:
"2|1:0|10:1476412069|5:count|4:NQ==|cb5fc1d4434971de6abf87270ac33381c686e4ec8c6f7e62130a0f8cbe5b7609"
6c550611300a9c4015f6c116d81b2794c599c7a24ffbc1d3f0d8ff0881287a4b
字段说明:
- 安全cookie的版本,默认使用版本2,不带长度说明前缀
- 默认为0
- 时间戳
- cookie名
- base64编码的cookie值
- 签名值,不带长度说明前缀
注意:
Tornado的安全cookie只是一定程度的安全,仅仅是增加了恶意修改的难度。Tornado的安全cookies仍然容易被窃听,而cookie值是签名不是加密,攻击者能够读取已存储的cookie值,并且可以传输他们的数据到任意服务器,或者通过发送没有修改的数据给应用伪造请求。因此,避免在浏览器cookie中存储敏感的用户数据是非常重要的。
02登录验证
继续回到上个问题,我们希望用户只需要在第一次登录的时候输入用户名和密码,之后可以自动登录,不需要再次输入用户名和密码,也就是说在用户第二次访问的时候,服务器能够自动的验证用户登录信息,那么如何实现自动验证的功能呢?
用户登录验证是指在收到用户请求后进行处理前先判断用户的认证状态(如登陆状态),若通过验证则正常处理,否则强制用户跳转至认证页面(如登陆页面)。
第一步:导入装饰器
from tornado.web import authenticated
第二步:声明 BaseHandler
装饰器@authenticated的判断执行依赖于请求处理类中的self.current_user属性
在获取self.current_user属性的时候,tornado会调用get_current_user()方法来返回current_user的值
我们验证用户的逻辑应写在get_current_user()方法中,若该方法返回非假值则验证通过,否则验证失败。
class Basehandler(tornado.web.RequestHandler):
def get_current_user(self):
current_user = self.get_secure_cookie('username')
if current_user:
return current_user
return None
第三步:配置登录路由
当用户验证失败时,将用户重定向到 login_url 上,所以我们还需要在Application中配置login_url。
login_url='/login',
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render('06cookie.html') # 跳转到登录页面
def post(self):
name = self.get_argument('name', 'no') # 获取模板中的用户名
user = User.by_name(name) # 查询数据库中是否有此用户
password = self.get_argument('password', 'no') # 获取模板中的密码
if user and password == user.password:
self.set_secure_cookie('username', name) # 设置此用户username的cookie
self.render('07login.html', username=name)
else:
self.write('用户名或密码错误')-
----------------------------------------------------------------------------------
路由
(r'/login', LoginHandler),
------------------------------------------------------------------------------------
user_module.py
@classmethod
def by_name(cls, name):
return session.query(cls).filter(cls.username == name).first()
第四步:装饰需要验证的请求
为了使用Tornado的认证功能,我们需要对登录用户标记具体的处理函数。
我们可以使用@tornado.web.authenticated装饰器完成它。
当我们使用这个装饰器包裹一个处理方法时,Tornado将确保这个方法的主体只有在合法的用户被发现时才会调用。
class BuyHandler(Basehandler):
@authenticated
def get(self):
self.write('恭喜你成功支付100000元')
代码
06cookie.py
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
import time
from tornado.options import define, options
from tornado.web import authenticated
from data.user_module import User
define(name='port', default=8000, type=int, help='run port')
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
current_user = self.get_secure_cookie('username')
if current_user:
return current_user
return None
class BuyHandler(BaseHandler):
@authenticated
def get(self):
self.write('222333')
class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.render('06cookie.html') # 跳转到登录页面
def post(self):
name = self.get_argument('name', 'no') # 获取模板中的用户名
user = User.by_name(name) # 查询数据库中是否有此用户
password = self.get_argument('password', 'no') # 获取模板中的密码
if user and password == user.password:
self.set_secure_cookie('username', name) # 设置此用户username的cookie
self.render('07login.html', username=name)
else:
self.write('用户名或密码错误')
application = tornado.web.Application(
handlers=[
(r'/buy', BuyHandler),
(r'/login', LoginHandler),
],
template_path='template',
cookie_secret='R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM=',
login_url='/login',
debug=True
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
07login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
欢迎{{ username }}登录
</body>
</html>
06cookie.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello world
<form method="post" action="/login">
<p>用户名:<input type="text" name="name"></p>
<p>密 码:<input type="password" name="password"></p>
<input type="submit">
</form>
</body>
</html>
user_module.py
from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
from .connect import Base, session
from sqlalchemy.orm import relationship
# 实体类User,对应数据库中的user表
class User(Base):
# 表的名字:
__tablename__ = 'user'
# 表的结构:
id = Column(Integer, primary_key=True, autoincrement=True) # 主键 自增
username = Column(String(20), nullable=False) # 不为空
password = Column(String(50))
creatime = Column(DateTime, default=datetime.now)
_locked = Column(Boolean, default=False, nullable=False)
@classmethod
def by_name(cls, name):
return session.query(cls).filter(cls.username == name).first()
def __repr__(self):
return "<User(id=%s,username=%s,password=%s,creatime=%s)>" % (
self.id,
self.username,
self.password,
self.creatime
)
if __name__ == '__main__':
# 找到BaseModel的所有子类,并在数据库中建立这些表
Base.metadata.create_all()
访问 http://127.0.0.1:8080/buy
自动转到登录页面 http://127.0.0.1:8080/login?next=%2Fbuy
填写表单 登录成功后 自动转 http://127.0.0.1:8080/login
访问 http://127.0.0.1:8080/buy
显示 恭喜你成功支付100000元
注意:/ 的 ascii码 是 2F
问题一:
在完成登录之后,再来看看,我们从一个路由跳转到登录页面之后,再完成登录之后,该如何跳转到之前的页面呢?
自动转到登录页面 http://127.0.0.1:8080/login?next=%2Fbuy
登陆成功后 自动转到之前的页面 http://127.0.0.1:8080/buy
第一步:获取之前路由
class LoginHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
next = self.get_argument('next')
print(next)
self.render('06cookie.html', nextname=next, error=None)
在使用 authenticated 之后,如果验证不成功,会自动跳转到登录路由,并且在 URL 后面加上 next 参数,next 参数的参数值就是之前的路由(/buy)
第二步:修改模板文件
{% if error %}
{{ error }}
{{nextname}}
{% end %}
<form method="post" action="/login?next={{ nextname }}">
在模板中添加 next 参数,并且参数值为之前的路由
第三步:修改post方法
def post(self):
name = self.get_argument('name', 'no')
user = User.byname(name) # 去数据库中查询是否存在此name 返回 User对象 or None
password = self.get_argument('password', 'no')
next = self.get_argument('next', 'None') # /buy
if user and password == user.password:
self.set_secure_cookie('username', name) # 设置此用户username的cookie
self.redirect(next) # 重定向 /buy 路由
else:
self.render('06cookie.html', nextname=next, error='用户名或密码错误')
获取之前的页面的路由
当登录验证通过之后,设置加密的 cookie ,并跳转到之前的路由
解析
访问 http://127.0.0.1:8080/buy
因BuyHandler类加了@authenticated装饰器 故需要进行验证
若有登录 就正常访问 若没有登录 就需要先登录
自动转到登录验证页面 http://127.0.0.1:8080/login?next=%2Fbuy
render('06cookie.html', nextname=next, error=None)
同时获取url中的next的值 传值给模板中的nextname
填写表单 表单提交到 /login?next={{ nextname }} 即 /login?next=/buy
1 若登录验证失败 (此时页面url:http://127.0.0.1:8080/login?next=/buy)
render('06cookie.html', nextname='/login', error='用户名或密码错误')
再跳转到登录验证页面 提示 用户名或密码错误
2 若登陆验证成功后
redirect(next) 即 redirect(/buy)
跳转到之前的页面 http://127.0.0.1:8080/buy
代码
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
import time
from tornado.options import define, options
from tornado.web import authenticated
from data.user_module import User
define(name='port', default=8000, type=int, help='run port')
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
current_user = self.get_secure_cookie('username')
if current_user:
return current_user
return None
class BuyHandler(BaseHandler):
@authenticated
def get(self, *args, **kwargs):
self.write('222333')
class LoginHandler(tornado.web.RequestHandler):
def get(self):
next = self.get_argument('next')
self.render('06cookie.html', nextname=next, error=None)
def post(self):
name = self.get_argument('name', 'no')
user = User.by_name(name)
password = self.get_argument('password', 'no')
next = self.get_argument('next','None')
print(next)
if user and password == user.password:
self.set_secure_cookie('username', name)
self.redirect(next)
else:
self.render('06cookie.html', nextname=next, error='用户名或密码错误')
application = tornado.web.Application(
handlers=[
(r'/buy', BuyHandler),
(r'/login', LoginHandler),
],
template_path='template',
cookie_secret='R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM=',
login_url='/login',
debug=True
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
06cookie.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if error %}
{{ error }}
{% end %}
<form method="post" action="/login?next={{ nextname }}">
<p>用户名:<input type="text" name="name"></p>
<p>密 码:<input type="password" name="password"></p>
<input type="submit">
</form>
</body>
</html>
03session
通过刚才的学习,可以用了解到 cookie 中的信息可以用来保存用户的登录信息,但是coolkie 是很容易被拦截的,所有其中必定不能有用户的任何私密信息,那么又有什么办法可以让服务器保存用户的登录信息,但是cookie中又不会有用户的任何信息呢?
cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生。
cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来判断这个是“谁”了。
cookie虽然在一定程度上解决了“保持状态”的需求,但是由于cookie本身最大支持4096字节,以及cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是session。
问题来了,基于http协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的cookie就起到桥接的作用。
我们可以给每个客户端的cookie分配一个唯一的id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
总结而言:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;但是cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过cookie识别不同的用户,对应的在session里保存私密的信息以及超过4096字节的文本。
session把用户登录信息存放在redis数据库中 cookie中存放没有任何意义的键值 保证用户信息不被情人泄露
redis存放在内存
MySQL存放在硬盘里
Cookie保存在客户端浏览器中,而Session保存在服务器上
客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。
客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
每个用户访问服务器都会建立一个session,那服务器是怎么标识用户的唯一身份呢?事实上,用户与服务器建立连接的同时,服务器会自动为其分配一个SessionId。
1、两个问题:
1)什么东西可以让你每次请求都把SessionId自动带到服务器呢?显然就是cookie了,如果你想为用户建立一次会话,可以在用户授权成功时给他一个唯一的cookie。当一个
用户提交了表单时,浏览器会将用户的SessionId自动附加在HTTP头信息中,(这是浏览器的自动功能,用户不会察觉到),当服务器处理完这个表单后,将结果返回给SessionId
所对应的用户。试想,如果没有 SessionId,当有两个用户同时进行注册时,服务器怎样才能知道到底是哪个用户提交了哪个表单呢。
2)储存需要的信息。服务器通过SessionId作为key,读写到对应的value,这就达到了保持会话信息的目的。
2、session的创建:
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了sessionId,如果已包含则说明以前已经为此客户端创建过session,服务
器就按照sessionId把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含sessionId,则为此客户端创建一个session并且生成一个与此session相关
联的sessionId,sessionId的值是一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionId将被在本次响应中返回给客户端保存。
总结
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中。
第一步:安装模块
pip install pycket
pip install redis
第二步:导入模块
from pycket.session import SessionMixin
第三步:继承SessionMixin
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
class LoginHandler(BaseHandler):
第四步:在application 中添加配置
pycket={
'engine': 'redis',
'storage': {
'host': 'localhost', # 地址
'port': 6379, # 端口
'db_sessions': 5, # 使用哪个库
'db_notifications': 11, # 通知
'max_connections': 2 ** 31, # 最大连接数
},
'cookies': {
'expires_days': 30, #过期时间 30天
'max_age': 100 * 60 # 最大过期时间 单位是s
},
},
配置 redis 的相关信息
配置 cookie 的过期时间
第五步:改设置cookie为设置session
self.session.set('username',name)
第六步:改获取cookie为获取session
current_user = self.session.get('username')
代码
import tornado.web
import tornado.ioloop
import tornado.options
import tornado.httpserver
import time
from tornado.options import define, options
from tornado.web import authenticated
from data.user_module import User
from pycket.session import SessionMixin
define(name='port', default=8000, type=int, help='run port')
class BaseHandler(tornado.web.RequestHandler, SessionMixin):
def get_current_user(self):
# current_user = self.get_secure_cookie('username')
current_user = self.session.get('username')
if current_user:
return current_user
return None
class BuyHandler(BaseHandler):
@authenticated
def get(self, *args, **kwargs):
self.write('222333')
class LoginHandler(BaseHandler):
def get(self, *args, **kwargs):
next = self.get_argument('next')
self.render('06cookie.html', nextname=next, error=None)
def post(self, *args, **kwargs):
name = self.get_argument('name', 'no')
user = User.by_name(name) # 去数据库中查询是否存在此name 返回 User对象 or None
password = self.get_argument('password', 'no')
next = self.get_argument('next', 'None') # /buy
print(next)
if user and password == user.password:
# self.set_secure_cookie('username', name)
self.session.set('username', name) # 设置此用户username的cookie
self.redirect(next) # 重定向 /buy 路由
else:
self.render('06cookie.html', nextname=next, error='用户名或密码错误')
application = tornado.web.Application(
handlers=[
(r'/buy', BuyHandler),
(r'/login', LoginHandler),
],
template_path='template',
cookie_secret='R7+/B+0cRLifilTElmnkOw1hFqqzQkIJoPBMUNr/MbM=',
login_url='/login',
pycket={
'engine': 'redis',
'storage': {
'host': 'localhost', # 地址
'port': 6379, # 端口
'db_sessions': 5, # 使用哪个库
'db_notifications': 11, # 通知
'max_connections': 2 ** 31, # 最大连接数
},
'cookies': {
'expires_days': 30,
'max_age': 100
},
},
debug=True
)
if __name__ == '__main__':
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
04 XSRF
使用session可以保证用户信息不被cookie泄漏,那如果如果攻击者不想获取用户信息,只是在提交 form 表单时攻击,该怎么防范呢?
XSRF 跨站伪造请求
模板添加
{% module xsrf_form_html() %}
Tornado 有内建的 XSRF 的防范机制,要使用此机制,只需要在模板中添加如上代码
06cookie.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if error %}
{{ error }}
{% end %}
<form method="post" action="/login?next={{ nextname }}">
{% module xsrf_form_html() %}
<p>用户名:<input type="text" name="name"></p>
<p>密 码:<input type="password" name="password"></p>
<input type="submit">
</form>
</body>
</html>