Tornado(二)

跨站请求伪造CSRF

开启xsrf(就是叫法不一样和csrf一样),'xsrf_cookies':True 

settings = {
    'template_path':'template',
    'static_path':'static',
    'static_path_prefix':'/static/',
    'xsrf_cookies':True,
}

在post表单中增加csrf认证

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>index.html</h1>
<h1>{{ name }}</h1>
<form action="/index" method="post">
{% module xsrf_form_html() %} <p>user:<input type="text"/></p> <p>password:<input type="password" /> </p> <input type="submit" value="submit" /> </form> </body> </html>

网站请求效果(表单中没有增加认证token):

 

 

加上token

 

 

AJAX方法

官方提供:

获取cookie的token信息  args._xsrf = getCookie("_xsrf");
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 + ")"));
    }});
};

 

 

 

UI(自定义标签)

自定义有两种方式:uimethods(方法)和uimodule(模块)

创建玩一下uimethods,新创建一个py文件

#_*_coding:utf-8_*_


def abcd(self):
    return '自定义uimethod方法'
uimethods

主文件

#_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web
#导入UImethod
import uimethods #'ui_methods':uimethods 设置下即可 settings = { 'template_path':'template', 'static_path':'static', 'static_path_prefix':'/static/', 'ui_methods':uimethods } class MainHandler(tornado.web.RequestHandler): def get(self): dic={'name':'abc'} self.render('index.html',**dic) app=tornado.web.Application([ (r"/index", MainHandler), ],**settings) if __name__ == "__main__": app.listen(8888) tornado.ioloop.IOLoop.current().start()

html页面中加入自定方法即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>index.html</h1>
//自定义方法
{{abcd()}}

</body>
</html>

演示效果

 

自定义uimedule(代码改动不多)

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>index.html</h1>
<p>{{abcd()}}</p>
<p>自定义module</p>
//这里可以传递参数
{% module test_mod(123) %}
</body>
</html>
html

uimodules文件

#_*_coding:utf-8_*_
from tornado.web import UIModule

class test_mod(UIModule):
    #render是死的 最后返回的就是这个方法
    def render(self, *args, **kwargs):
        return 'uimodule.', args
uimodules

主文件

#_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web
import uimethods
import uimodules


settings = {
    'template_path':'template',
    'static_path':'static',
    'static_path_prefix':'/static/',
    'ui_methods':uimethods,
    'ui_modules':uimodules,
}

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        dic={'name':'abc'}
        self.render('index.html',**dic)
app=tornado.web.Application([
        (r"/index", MainHandler),
    ],**settings)

if __name__ == "__main__":
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()
View Code

运行效果:

 自定义当方法在以后的应用是很广泛的,tornado 的方法不熟悉,完全可以用这个方法自己写

 

 

 

 

用户认证

简单说一下session和cookie 关系

      由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。
简图:

上图可以看出cookie中不会存在敏感信息,重要的信息存储在服务器端的session中

 Tornado是没有session 的,如下图这么处理的

 

 

这个方法不怎么安全,连接Tornado机制的就很容易伪造

 

 实现tornado源生验证(很少会用这个方式来完成登陆验证)

主文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):

    def get(self):
        #从cookie中获取key=login_user的值
        login_user = self.get_secure_cookie("login_user", None)
        if login_user:
            #获取cookie成功就打印这个登陆名
            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):
        #获取前端传递过来的 name和password
        username = self.get_argument('username')
        password = self.get_argument('password')
        if username == 'lily0912' and password == '123':
            #登陆成功就设置cookie,key=login_user,值=lily0912
            self.set_secure_cookie('login_user', 'lily0912')
            #跳转到首页
            self.redirect('/index')
        else:
            self.render('login.html', **{'status': 'name or password error!!!!'})

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'asdassdasdsd123'
}

application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>登陆</h1>
<form action="/login" method="post">
    <p>user:<input name="username" type="text"/></p>
    <p>password:<input name="password" type="password" /> </p>
    <input type="submit" value="submit" />
</form>
</body>
</html>
login

验证:

 登陆成功后跳转页面,然后可以看到服务器给客户端的cookie信息。

 自定义session验证

 

写cookie过程:

  • 将值进行base64加密
  • 对除值意外的内容进行签名,哈希算法(无法逆向解析)
  • 拼接 签名 + 加密值

读cookie过程:

  • 读取 签名 + 加密值
  • 对签名进行验证
  • base64解密,获取值内容

所以,我们会将base64加密的值返回给用户。而对于session,他只会将签名返回给用户,然后根据签名获取redis或数据库中保存的其他值。即:在session中,签名和session值的集合是一一对应的。

 

python魔法方法(为tornado 增加session功能就靠它了)

简单说下这个例子是python模仿字典工作

class Foo(object):
  def __init__(self, key, value):
    self.key = []
    self.value = []
    self.key.append(key)
    self.value.append(value)

  def __len__(self):
    return len(self.key)

  def __getitem__(self, item):
    try:
      __index = self.key.index(item)
      return self.value[__index]
    except ValueError:
      raise KeyError('can not find the key')

  def __setitem__(self, key, value):
    if key not in self.key:
      self.key.append(key)
      self.value.append(value)
    else:
      __index = self.key.index(key)
      self.value[__index] = value

  def __delitem__(self, key):
    try:
      __index = self.key.index(key)
      del self.key[__index]
      del self.value[__index]
    except ValueError:
      raise KeyError('can not find the key')

  def __str__(self):
    result_list = []
    for index in xrange(len(self.key)):
      __key = self.key[index]
      __value = self.value[index]
      result = __key, __value
      result_list.append(result)
    return str(result_list)

  def __iter__(self):
    self.__index = 0
    return self

  def next(self):
    if self.__index == len(self.key):
      self.__index = 0
      raise StopIteration()
    else:
      __key = self.key[self.__index]
      __value = self.value[self.__index]
      result = __key, __value
      self.__index += 1
      return result

  def __reversed__(self):
    __result = self.value[:]
    __result.reverse()
    return __result

  def __contains__(self, item):
    if item in self.value:
      return True
    else:
      return False


a = Foo('scolia', 'good')
a[123] = 321
a[456] = 654
a[789] = 987
print a.key
print len(a)
del a[789]
print a
for x, y in a:
  print x, y
print reversed(a)
print 123 in a
print 321 in av
演示代码
['scolia', 123, 456, 789]
4
[('scolia', 'good'), (123, 321), (456, 654)]
scolia good
123 321
456 654
[654, 321, 'good']
False
True
执行结果

这个东西没有弄过的的熟悉一下。确实有的魔法的感觉。

 

 

Tornado 实现session

显示熟悉一下流程

application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings)

根据url匹配都会找到对应的类比如:class LoginHandler(tornado.web.RequestHandler)  

都会继承这个tornado.web.RequestHandler父类,首先父类会初始化RequestHandler.__init__() ,最后还调用self.initialize(**kwargs)了这个类。这个里面什么都没有。主要是留给我扩展用的 

最后执行LoginHandler 类中的方法

我们有做的就是在执行LoginHandler方法之前  生成session 就OK了。

可以这么做:

自己写个基类继承tornado.web.RequestHandler 
class
BaseHandler(tornado.web.RequestHandler): #继承后直接改写这个方法就行
def initialize(self):
在这里写session内容就OK了
pass 下面的子类直接继承BaseHandler class MainHandler(BaseHandler): class LoginHandler(BaseHandler):

 

OK现在开始实现session

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time

#生成session id
session_id=lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()

class Session(object):
    def __init__(self,obj):
        self.obj=obj
    def __getitem__(self, key):
        pass
    def __setitem__(self, key, value):
        #创建session_id
        token=session_id()
        #设置cookie的 key这个无所谓,起个名就行
        cookie_key='_session'
        self.obj.set_secure_cookie(cookie_key,token)
    def __delitem__(self, key):
        pass


class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        #将self传递给session方法,原因很简单Session方法本身无法将cookie写入LoginHandle对象中。因为它没有这个方法
        self.mysesson=Session(self)

class MainHandler(BaseHandler):

    def get(self):
        #从cookie中获取key=login_user的值
        login_user = self.get_secure_cookie("login_user", None)
        if login_user:
            #获取cookie成功就打印这个登陆名
            self.write(login_user)
        else:
            self.redirect('/login')

#RequestHandler.__init__()实例化方法
class LoginHandler(BaseHandler):
    def get(self):
        #self.current_user()
        self.render('login.html', **{'status': ''})

    def post(self, *args, **kwargs):
        username = self.get_argument('username')
        password = self.get_argument('password')
        if username == 'lily0912' and password == '123':
            #self.set_secure_cookie('login_user', 'lily0912')
            #成功登陆就设置状态True
            self.mysesson['login_status']='True'
            self.redirect('/index')
        else:
            self.render('login.html', **{'status': 'name or password error!!!!'})

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'asdassdasdsd123'
}

application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

 

看下session是否写入成功

 

posted @ 2016-10-17 11:31  MKY-门可意  阅读(370)  评论(0编辑  收藏  举报