xsrf_token认证 -- 防止CSRF
可以通过一个Cookie和一个隐藏的HTML表单元素向页面提供令牌。这样,当一个合法页面的表单被提交时,它将包括表单值和以存储的Cookie。如何两者匹配,则Tornado应用认定请求有效。
开启CSRF防范功能需要两个步骤:
1.实例化的时候传入 "xsrf_cookies": True
参数。
settings={
'template_path':'templates',#配置模板路径
'static_path':'static', #配置静态文件存放的路径
'static_url_prefix':'/zhanggen/', #在模板中引用静态文件路径时使用的别名 注意是模板引用时的别名
"xsrf_cookies": True, #使用xsrf认证
}
2.在有表单的模板文件中,为所有表单条件 xsrf_form_html()
函数标签。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href='{{static_url("dist/css/bootstrap.css") }}'>
<title>Title</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-5 col-md-offset-3">
<form method="post" >
{% raw xsrf_form_html() %}
<div class="form-group">
<input type="text" class="form-control" placeholder="用户名" name="user">
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="密码" name="pwd">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</div>
</div>
</div>
</body>
</html>
{% raw xsrf_form_html() %}
起到了为表单添加隐藏元素以防止跨站请求的左右。
Cookie 安全机制
cookie读写
tornado不自动session,但是包含cookie
Cookie是很多网站辨别用户的身份而存储在用户本地终端(client Side)的数据。在Tornado中使用self.get_cookie()
、self.set_cookie()
可以方便的对Cookie进行读写。
import tornado.web
session_id = 1
class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_cookie("session"):
t = time.time()+10
self.set_cookie("session", str(session_id), expires=t) # 并且设置了过期时间
self.write("设置了一个新的session!")
else:
self.write("session已存在!")
在实际应用中,cookie经常用于保存session的信息。
可以设置在用户不断刷新页面的情况下,cookie不过期。
构造initialize
方法:
import tornado.web
session_id = 1
class MainHandler(tornado.web.RequestHandler):
def initialize(self):
if not self.get_cookie("session"):
t = time.time()+10
self.set_cookie("session", str(session_id), expires=t) # 并且设置了过期时间
self.write("设置了一个新的session!")
else:
self.write("session已存在!")
cookie加密
因为cookie总数被保存在客户端,所有如何保证不被篡改是服务器端程序必须解决的问题。Tornado提供了为cookie信息加密的机制,使得客户端无法随意解析和修改cookie的键值。
在初始化时传入'cookie_secret' :'xsseffekrjewkhwy'
做cokies加密时使用的密钥,不能泄露出去。
用get_secute_cookie
替换get_cookie
, 用set_secure_cookie
替换set_cookie
,这样就不需要担心cookie伪造的问题了。
import tornado.web
import tornado.ioloop
settings={
"xsrf_cookies": True,
'cookie_secret':'xsseffekrjewkhwy' # 配置加密cookie使用得加密字符串
}
session_id = 1
class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_secute_cookie("session"):
t = time.time()+10
self.set_secure_cookie("session", str(session_id), expires=t) # 并且设置了过期时间
self.write("设置了一个新的session!")
else:
self.write("session已存在!")
app = tornado.web.Application([
(r"/", MainHandler)],
**settings
)
if __name__ == "__main__":
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
@authenticated 用户认证
在RequestHandler类中有一个current_user
属性用于保存当前请求的用户名,默认值是None,在get()、post()等处理函数中可以随时读取该属性可以获得当前的用户名。
需要重写get_current_user()
方法来设置该属性值。
基本框架代码:
import tornado.web
import tornado.ioloop
class BaseHandler(tornado.web.RequestHandler):
# 基类,重写get_current_user方法
def get_current_user(self):
return self.get_secure_cookie("session_id")
class MainHandler(BaseHandler):
@tornado.web.authenticated # 能够检测用户是否登录
def get(self):
name = tornado.escape.xhtml_escape(self.current_user)
self.write("Hello, " + name)
class LoginHandler(BaseHandler):
def get(self):
self.render("login.html")
def post(self):
name = self.get_argument("user")
self.set_secure_cookie("session_id", name)
self.redirect("/")
settings={
'template_path':'templates',
'static_path': 'static',
'static_url_prefix':'/static/',
'cookie_secret':'sssseertdfcvcvd'
'login_url':'/login' # @authenticated 验证失败跳转的url
}
app = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler)],
**settings
)
if __name__ == "__main__":
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
框架解读:
上面是一个较完整的身份认证框架(可完善:用户session保存数据库,密码验证机制等。):
- 定义公共基类BaseHandler,继承tornado.web.RequestHandler,用于动议本网站所有处理器的公共属性和方法。重构
get_current_user()
方法,获取并返回本次访问的会话ID。 - MainHandler类是一个要求用户经过身份认证才能访问的视图。 get()方法使用了装饰器
tornado.web.authenticated
,说明在执行该方法之前需要根据current_user
是否有值来判断用户的身份认证情况。如果有值则可以进行正常逻辑,否则自动重定向到登录页面(login_url的配置)。 - LoginHandler是登录逻辑的视图,get()用于渲染登录页面,post()用于用户登录验证。
settings
中的login_url
参数是定义网站的登录页面地址。当tornado.web.authenticated
发现用户尚未认证时,重定向到定义的URL。
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href='{{static_url("dist/css/bootstrap.css") }}'>
<title>Title</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-5 col-md-offset-3">
<form method="post" >
{% raw xsrf_form_html() %}
<div class="form-group">
<input type="text" class="form-control" placeholder="用户名" name="user">
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="密码" name="pwd">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</div>
</div>
</div>
</body>
</html>