tornado - cookie与认证相关

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读写

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>
posted @ 2020-08-27 12:59  SensorError  阅读(191)  评论(0编辑  收藏  举报