web 框架4 cookie session再次介绍 tornado自定义组件 session验证 图片验证码组件 csrf xss等介绍 文件上传的方式

1  cookie介绍1 cookie验证机制 cookie的写入方式两种

  本质为在浏览器端保存的键值对,由服务端写在浏览器端,以后每次请求的时候,浏览器都携带着cookie来访问,cookies的使用之处非常多,比如用户验证,登陆界面,右侧菜单隐藏,控制页面列表显示条数等,已经后面的session都是基于cookie的。cookie从设置方面来说可以由tronado和前端js设置

tornado设置(普通字符串,tronado做了分割等处理)

  self.cookies
  self.get_cookie('k1')
  self.set_cookie('k2', 'v2')
浏览器js设置
  document.cookie
  document.cookie.split(";") 获取所有的cookie列表
  document.cookie = "k3=66" 设置
  document.cookie = "k3=66;path='/"

1.0 如果有人问你什么是cookie ,cookie和session区别是什么

如下=====start

cookie :
    存在于浏览器的键值对,用于验证服务端身份信息的
    存放的值是一个随机字符串(token) 下次会带着这个token来,说明你的身份

session :  数据信息 可以放在内存,缓存,数据库
    是一个验证机制,依赖于cookie
    用于保存用户的敏感身份信息
    如果服务器性能 可以将一些不重要的信息放在cookie

    生成随机字符串:
        服务端

======= 下面的 为正规

cookie session区别:
    cookie放在客户端
    session的用户信息放在内存,缓存,数据库

cookie session联系:
       session的数据信息是我们人为创建的。
                是依据cookie创建的。

       用户来了给用户一个token,下次用户来还带着这个token

  

 

如上=====end

 

1.1 签名cookie本质

签名cookie

Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中:

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_secure_cookie("mycookie"):
            self.set_secure_cookie("mycookie", "myvalue")
            self.write("Your cookie was not set yet!")
        else:
            self.write("Your cookie was set!")
             
application = tornado.web.Application([
    (r"/", MainHandler),
], 
cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")

还可以写成 :

settings = {                cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=" 
}
application = tornado.web.Application([
    (r"/", MainHandler),
], **settings)
tornado 启动文件
def _create_signature_v1(secret, *parts):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
    for part in parts:
        hash.update(utf8(part))
    return utf8(hash.hexdigest())

# 加密
def _create_signature_v2(secret, s):
    hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
    hash.update(utf8(s))
    return utf8(hash.hexdigest())

def create_signed_value(secret, name, value, version=None, clock=None,
                        key_version=None):
    if version is None:
        version = DEFAULT_SIGNED_VALUE_VERSION
    if clock is None:
        clock = time.time

    timestamp = utf8(str(int(clock())))
    value = base64.b64encode(utf8(value))
    if version == 1:
        signature = _create_signature_v1(secret, name, value, timestamp)
        value = b"|".join([value, timestamp, signature])
        return value
    elif version == 2:
        # The v2 format consists of a version number and a series of
        # length-prefixed fields "%d:%s", the last of which is a
        # signature, all separated by pipes.  All numbers are in
        # decimal format with no leading zeros.  The signature is an
        # HMAC-SHA256 of the whole string up to that point, including
        # the final pipe.
        #
        # The fields are:
        # - format version (i.e. 2; no length prefix)
        # - key version (integer, default is 0)
        # - timestamp (integer seconds since epoch)
        # - name (not encoded; assumed to be ~alphanumeric)
        # - value (base64-encoded)
        # - signature (hex-encoded; no length prefix)
        def format_field(s):
            return utf8("%d:" % len(s)) + utf8(s)
        to_sign = b"|".join([
            b"2",
            format_field(str(key_version or 0)),
            format_field(timestamp),
            format_field(name),
            format_field(value),
            b''])

        if isinstance(secret, dict):
            assert key_version is not None, 'Key version must be set when sign key dict is used'
            assert version >= 2, 'Version must be at least 2 for key version support'
            secret = secret[key_version]

        signature = _create_signature_v2(secret, to_sign)
        return to_sign + signature
    else:
        raise ValueError("Unsupported version %d" % version)

# 解密
def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
    parts = utf8(value).split(b"|")
    if len(parts) != 3:
        return None
    signature = _create_signature_v1(secret, name, parts[0], parts[1])
    if not _time_independent_equals(parts[2], signature):
        gen_log.warning("Invalid cookie signature %r", value)
        return None
    timestamp = int(parts[1])
    if timestamp < clock() - max_age_days * 86400:
        gen_log.warning("Expired cookie %r", value)
        return None
    if timestamp > clock() + 31 * 86400:
        # _cookie_signature does not hash a delimiter between the
        # parts of the cookie, so an attacker could transfer trailing
        # digits from the payload to the timestamp without altering the
        # signature.  For backwards compatibility, sanity-check timestamp
        # here instead of modifying _cookie_signature.
        gen_log.warning("Cookie timestamp in future; possible tampering %r",
                        value)
        return None
    if parts[1].startswith(b"0"):
        gen_log.warning("Tampered cookie %r", value)
        return None
    try:
        return base64.b64decode(parts[0])
    except Exception:
        return None


def _decode_fields_v2(value):
    def _consume_field(s):
        length, _, rest = s.partition(b':')
        n = int(length)
        field_value = rest[:n]
        # In python 3, indexing bytes returns small integers; we must
        # use a slice to get a byte string as in python 2.
        if rest[n:n + 1] != b'|':
            raise ValueError("malformed v2 signed value field")
        rest = rest[n + 1:]
        return field_value, rest

    rest = value[2:]  # remove version number
    key_version, rest = _consume_field(rest)
    timestamp, rest = _consume_field(rest)
    name_field, rest = _consume_field(rest)
    value_field, passed_sig = _consume_field(rest)
    return int(key_version), timestamp, name_field, value_field, passed_sig


def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
    try:
        key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
    except ValueError:
        return None
    signed_string = value[:-len(passed_sig)]

    if isinstance(secret, dict):
        try:
            secret = secret[key_version]
        except KeyError:
            return None

    expected_sig = _create_signature_v2(secret, signed_string)
    if not _time_independent_equals(passed_sig, expected_sig):
        return None
    if name_field != utf8(name):
        return None
    timestamp = int(timestamp)
    if timestamp < clock() - max_age_days * 86400:
        # The signature has expired.
        return None
    try:
        return base64.b64decode(value_field)
    except Exception:
        return None


def get_signature_key_version(value):
    value = utf8(value)
    version = _get_version(value)
    if version < 2:
        return None
    try:
        key_version, _, _, _, _ = _decode_fields_v2(value)
    except ValueError:
        return None

    return key_version

内部算法
内部算法

个人理解的签名cookie

COOKIE:
    cookie是存放在浏览器的键值对。
        可以用来做用户验证,但是保存在浏览器的信息太多,所以后面很多用户信息放在服务端,浏览器是一个
        随机字符串加作为验证信息。
    cookie验证机制
        服务端 设置cookie验证的时候 (比较靠谱的验证,而不用担心被人一眼看到浏览器觉得cookie就能请求)
            1传入值x在tornado内部用base64加密加密后的值比如说是y,
                即 y = base64(x)
                比如说将这个y作为要发送给浏览器的cookie值得一部分,然后用|分割。
            2 时间戳也作为要写入浏览器的cookie值得一部分,然后用|分割。

            3 然后将 y值  时间戳, 还有自定义秘钥这三部分进行了单向加密 md5 或者hmac 作为要写入浏览器的cookie值得一部分,然后用|分割。

        如上三部分 用|分割作为写入浏览器的cookie的value
        浏览器再次请求的时候就会带着这个cookie信息来验证,
        服务端会将y值根据base64解密成一个值跟自己的x进行对比,然后,根据服务端自己的y值,自己的时间戳
        自己的自定义秘钥这三个再次单向加密md5或者hmac 与浏览器传过来的cookie的除了y值 时间戳的那个值
        进行对比,如果相等就代表浏览器通过了。

签名Cookie的本质是:

写cookie过程:

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

读cookie过程:

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

注:许多API验证机制和安全cookie的实现机制相同。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import tornado.ioloop
import tornado.web
 
 
class MainHandler(tornado.web.RequestHandler):
 
    def get(self):
        login_user = self.get_secure_cookie("login_user", None)
        if login_user:
            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):
 
        username = self.get_argument('name')
        password = self.get_argument('pwd')
        if username == 'wupeiqi' and password == '123':
            self.set_secure_cookie('login_user', '武沛齐')
            self.redirect('/')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})
 
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh'
}
 
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)
 
 
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

基于Cookie实现用户验证-Demo
基于Cookie实现用户验证-Demo
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
import tornado.ioloop
import tornado.web
 
class BaseHandler(tornado.web.RequestHandler):
 
    def get_current_user(self):
        return self.get_secure_cookie("login_user")
 
class MainHandler(BaseHandler):
 
    @tornado.web.authenticated
    def get(self):
        login_user = self.current_user
        self.write(login_user)
 
 
 
class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.current_user()
 
        self.render('login.html', **{'status': ''})
 
    def post(self, *args, **kwargs):
 
        username = self.get_argument('name')
        password = self.get_argument('pwd')
        if username == 'wupeiqi' and password == '123':
            self.set_secure_cookie('login_user', '武沛齐')
            self.redirect('/')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})
 
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}
 
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)
 
 
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

基于签名Cookie实现用户验证-Demo
基于签名Cookie实现用户验证-Demo

 

1.2  设置cookie方式

1.2.1  服务端写入到浏览器

   1 服务端写入到浏览器
        self.set_cookie
        常用的
            self.cookies #查看所有cookie
            slef。set_cookie(key,value,expire=day,domain=/) 影响的路径  过期时间
            self。get_cookie 获取cookie

1.2.2 通过js 或者jquery写入 按天和按月 ,但是js可以在浏览器禁用 。cookie可以禁用

需要先在网上下载 jQuery。cookie。js 这个文件

        <script src="/statics/jquery.cookie.js"></script>
        <script src="/statics/jquery-1.12.4.js"></script>

        <script>
            // javascripts方式 在浏览器设置cookie
            function setCookieBySeconds(name,value,expires) {
                var current_date = new Date();
                current_date.setDate(current_date.getSeconds() + expires);
                document.cookie = name + "= " +value +";expires=" + current_date.toUTCString()

            };
            function setCookieByDays(name,value,expires) {

                var current_date = new Date();
                current_date.setDate(current_date.getDay() + expires);
                document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间

            }
            // 调用
            // setCookieBySeconds('k1','v1',5) // 5秒过期
            // setCookieByDays('k1','v1',5) // 5天过期

            //###################jQuery设置###################
            $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期

            //如果不是按天的话,需要先定义时间秒变量
            var  current_date = new Date();
            current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s
            $.cookie('k1',"v1",{expires:current_date}) //6s 后过期
        </scrips>

对于参数:

  • domain   指定域名下的cookie
  • path       域名下指定url中的cookie
  • secure    https使用

注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里

1.2.3 cookie 保存周期

cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这

个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。

会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie

保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏

览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式

 

1.2.4 初始cookie,设置cookie的两种方式 后端写入和前端js jQuery设置项目代码

刚开始的cookie未签名

 

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

import  tornado.web
import tornado.ioloop
"""

cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这

个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。

会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie

保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏

览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式




COOKIE:
    cookie是存放在浏览器的键值对。
        可以用来做用户验证,但是保存在浏览器的信息太多,所以后面很多用户信息放在服务端,浏览器是一个
        随机字符串加作为验证信息。
    cookie验证机制
        服务端 设置cookie验证的时候 (比较靠谱的验证,而不用担心被人一眼看到浏览器觉得cookie就能请求)
            1传入值x在tornado内部用base64加密加密后的值比如说是y,
                即 y = base64(x)
                比如说将这个y作为要发送给浏览器的cookie值得一部分,然后用|分割。
            2 时间戳也作为要写入浏览器的cookie值得一部分,然后用|分割。

            3 然后将 y值  时间戳, 还有自定义秘钥这三部分进行了单向加密 md5 或者hmac 作为要写入浏览器的cookie值得一部分,然后用|分割。

        如上三部分 用|分割作为写入浏览器的cookie的value
        浏览器再次请求的时候就会带着这个cookie信息来验证,
        服务端会将y值根据base64解密成一个值跟自己的x进行对比,然后,根据服务端自己的y值,自己的时间戳
        自己的自定义秘钥这三个再次单向加密md5或者hmac 与浏览器传过来的cookie的除了y值 时间戳的那个值
        进行对比,如果相等就代表浏览器通过了。



设置cookie有两种:
    1 服务端写入到浏览器
        self.set_cookie
        常用的
            self.cookies #查看所有cookie
            slef。set_cookie(key,value,expire=day,domain=/) 影响的路径  过期时间
            self。get_cookie 获取cookie
    2 通过js 或者jquery写入 ,但是js可以在浏览器禁用 。cookie可以禁用
        <script src="/statics/jquery.cookie.js"></script>
        <script src="/statics/jquery-1.12.4.js"></script>

        <script>
            // javascripts方式 在浏览器设置cookie
            function setCookieBySeconds(name,value,expires) {
                var current_date = new Date();
                current_date.setDate(current_date.getSeconds() + expires);
                document.cookie = name + "= " +value +";expires=" + current_date.toUTCString()

            };
            function setCookieByDays(name,value,expires) {

                var current_date = new Date();
                current_date.setDate(current_date.getDay() + expires);
                document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间

            }
            // 调用
            // setCookieBySeconds('k1','v1',5) // 5秒过期
            // setCookieByDays('k1','v1',5) // 5天过期

            //###################jQuery设置###################
            $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期

            //如果不是按天的话,需要先定义时间秒变量
            var  current_date = new Date();
            current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s
            $.cookie('k1',"v1",{expires:current_date}) //6s 后过期
        </scrips>


对于参数:

domain   指定域名下的cookie
path       域名下指定url中的cookie
secure    https使用
注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里
"""

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        print(self.cookies)
        # self.set_cookie("k1","999",) #cookie 的value 是string
        self.set_secure_cookie("k1","999",) #cookie 的value 是string
        # print(self.get_cookie("k1"))
        print(self.get_secure_cookie("k1")) # 获取的是 byte字节类型 需要进行str格式化再比较
        self.render("index.html")

settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"/statics/", ###
    "cookie_secret":"dsfsfsafsdfasdf",
}

application = tornado.web.Application([
    (r"/index",IndexHandler),

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <h1>
        cookie有两种写入方式:
        <br>
            1 服务端写入到浏览器
        <br>
            2 浏览器自己通过js写入  document.cookie
    </h1>
</div>

<script src="/statics/jquery.cookie.js"></script>
<script src="/statics/jquery-1.12.4.js"></script>

<script>
    // javascripts方式 在浏览器设置cookie
    function setCookieBySeconds(name,value,expires) {
        var current_date = new Date();
        current_date.setDate(current_date.getSeconds() + expires);
        document.cookie = name + "= " +value +";expires=" + current_date.toUTCString()

    };
    function setCookieByDays(name,value,expires) {

        var current_date = new Date();
        current_date.setDate(current_date.getDay() + expires);
        document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间

    }
    // 调用
    // setCookieBySeconds('k1','v1',5) // 5秒过期
    // setCookieByDays('k1','v1',5) // 5天过期

    //###################jQuery设置###################
    $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期

    //如果不是按天的话,需要先定义时间秒变量
    var  current_date = new Date();
    current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s
    $.cookie('k1',"v1",{expires:current_date}) //6s 后过期


</script>

</body>
</html>
index.html

初步签名的项目代码

 

  前端的index基本相同

  只贴app.py

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

import  tornado.web
import tornado.ioloop
"""
较 前面我们可以用cookie 在客户端,我们在服务端封装很多用户信息。
"""
container = { } # 封装用户信息的放在程序的全局变量中,如果放在局部变量,当一个用户访问完后,程序的内部变量会销毁。

# 有个弊端 ,1个用户每次请求这个页面的时候都会重新生成随机字符串的cookie值。这样不好。
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
            import hashlib
            import time
            obj = hashlib.md5()
            obj.update(bytes(str(time.time()),encoding="utf-8"))
            random_str = obj.hexdigest()
            container[random_str] = {} #以随机字符串为key
            #我希望以后以 is_login来判断是否已经登录
            container[random_str]["is_login"] = True
            container[random_str]['k1'] = self.get_argument("u",None)+ "parents"
            container[random_str]["k2"] = 123
            self.set_cookie("__session__",random_str) # 将随机字符串作为key传给客户端 内部会执行之前说过的cookie加密过程
            self.write("cookie设置成功了")

        else:
            self.write("请登录")

class ManaggerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        print(self.get_cookie("__session__"))
        random_str = self.get_cookie("__session__")
        current_user_info = container.get(random_str,None) #get 获取字典的key 没有则返回None
        if not current_user_info:
            self.redirect("/index")
        else:
            if current_user_info.get("is_login",None):
                temp = "%s- %s" %(current_user_info.get("k1",""),current_user_info["k2"]) # 获取k1的值,获取不到给个空,如果为none字符串格式化的时候回报错
                self.write(temp)
            else:
                self.redirect("/index")


settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"/statics/", ###
    "cookie_secret":"dsfsfsafsdfasdf",
}

application = tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManaggerHandler),

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py

 

1.2.5 COOKIE 签名 做成一套session机制 

上面已经介绍过cookie session了介绍

session是用户信息,可以放在内存,数据库 memcahe redis ,是依赖cookie。是一套机制。不是具体数据。

      cookie保存单一键值对,如果需要保存其他内容,则需要写多个cookie,而每次请求的话都会发送所有的cookie这样的话,会造成网络拥堵,此外,cookies是保存在浏览器端,如果保存用户名密码的cookie也放在浏览器端的话,也不够安全,这样就引出了session,session是人为生成的。session也是基于cookie来做的,但是只在浏览器端生成一个cookie(随机字符串,sessionId,token),而在服务端也保存着这段cookie,此外服务端还根据这段cookie可以生成一个字典,字典里面就可以放置用户的其他信息,服务端将这段cookie(sessionId)写到浏览器端,以后浏览器端来访问的时候,服务端根据sessionId从其相应的字典里面获取相应的信息来做相应用户认证机制。session可以保存在全局变量里,放在数据库,文件里面,memcached radis但是不能放在局部变量里。

cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这

个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。

会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie

保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏

览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式



session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

cookie 和session 的区别:

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
   考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
   考虑到减轻服务器性能方面,应当使用COOKIE。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、所以个人建议:
   将登陆信息等重要信息存放为SESSION
   其他信息如果需要保留,可以放在COOKIE中

1.2.6 自定义组件1(后面详细讲) session机制的cookie签名组件

同理上面已经介绍过了index.html自行复制即可

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

import  tornado.web
import tornado.ioloop
"""
cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这

个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。

会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie

保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏

览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式



session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

cookie 和session 的区别:

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
   考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
   考虑到减轻服务器性能方面,应当使用COOKIE。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、所以个人建议:
   将登陆信息等重要信息存放为SESSION
   其他信息如果需要保留,可以放在COOKIE中

"""
container = { } # 封装用户信息的放在程序的全局变量中,如果放在局部变量,当一个用户访问完后,程序的内部变量会销毁。
# 有个弊端 ,1个用户每次请求这个页面的时候都会重新生成随机字符串的cookie值。这样不好。
class Session(tornado.web.RequestHandler):
    def __init__(self,handler):
        self.handler = handler


    def __generate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding="utf-8"))
        random_str = obj.hexdigest()
        return random_str

    def set_value(self,key,vlaue):
        #在contain定义随机字符串,
        # 定义自己的数据
        #在浏览器写入随机字符串
        random_str = self.__generate_random_str()
        container[random_str] = {}  # 以随机字符串为key
        # 我希望以后以 is_login来判断是否已经登录
        container[random_str][key] = vlaue
        self.handler.set_cookie("__session__",random_str)

    def get_value(self,key):
        random_str = self.handler.get_cookie("__session__",None)
        print(random_str)
        user_info_dict = container.get(random_str,None) # 获取不到返回空
        if user_info_dict:
            value = user_info_dict[key]
            return value

# 有个弊端 ,1个用户每次请求这个页面的时候都会重新生成随机字符串的cookie值。这样不好。
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
            s = Session(self) #将 Indexhandler 类实例化对象传入
            s.set_value("is_login",True)
        else:
            self.write("请登录")

class ManaggerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        print(container)
        # print(self.get_cookie("__session__"))
        s = Session(self)
        val = s.get_value("is_login")
        if val:
            self.write("登录成功")
        else:
            self.write("登录失败")


settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"/statics/", ##
    "cookie_secret":"dsfsfsafsdfasdf",
}

application = tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManaggerHandler),

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py

加强完整验证版 app.py

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

import  tornado.web
import tornado.ioloop

"""
session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法


obj = class1()
obj() ====对象加括号调用的是类的__call__方法

如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法

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

class Foo(object):

    def __getitem__(self, key):
        print  '__getitem__',key

    def __setitem__(self, key, value):
        print '__setitem__',key,value

    def __delitem__(self, key):
        print '__delitem__',key



obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

"""
container = {}
# container = {
#     # "第一个人的随机字符串":{},
#     # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
# }

class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __genarate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        random_str = obj.hexdigest()
        return random_str

    def set_value(self, key,value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了
            random_str = self.handler.get_cookie('__session__')
            if not random_str:
                random_str = self.__genarate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串
                if random_str in container.keys():
                    pass
                else:
                    random_str = self.__genarate_random_str()
                    container[random_str] = {}
            self.random_str = random_str # self.random_str = asdfasdfasdfasdf

        container[self.random_str][key] = value
        self.handler.set_cookie("__session__", self.random_str)

    def get_value(self,key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        #  专属信息【key】
        random_str =  self.handler.get_cookie("__session__")
        if not random_str:
            return None
        # 客户端有随机字符串
        user_info_dict = container.get(random_str,None)
        if not user_info_dict:
            return None
        value = user_info_dict.get(key, None)
        return value

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
            s = Session(self) #将 Indexhandler 类实例化对象传入
            s.set_value("is_login",True)
            s.set_value("name",self.get_argument("u",None))
            print(container)
            self.write("cookie设置成功了")
        else:
            self.write("请登录")

class ManaggerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        print(container)
        # print(self.get_cookie("__session__"))
        s = Session(self)
        val = s.get_value("is_login")
        if val:
            self.write(s.get_value("name")+"登录成功")
        else:
            self.write("登录失败121212")


settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"/statics/",
    "cookie_secret":"dsfsfsafsdfasdf",
}

application = tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManaggerHandler),

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
验证完整版的app.py

改成面向对象的 session类调用cookie设置值

完整app 

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

import  tornado.web
import tornado.ioloop

"""
session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法


obj = class1()
obj() ====对象加括号调用的是类的__call__方法

如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法

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

class Foo(object):

    def __getitem__(self, key):
        print  '__getitem__',key

    def __setitem__(self, key, value):
        print '__setitem__',key,value

    def __delitem__(self, key):
        print '__delitem__',key



obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

"""
container = {}
# container = {
#     # "第一个人的随机字符串":{},
#     # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
# }

class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __genarate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        random_str = obj.hexdigest()
        return random_str

    # def set_value(self, key,value):
    def __setitem__(self, key, value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了
            random_str = self.handler.get_cookie('__session__')
            if not random_str:
                random_str = self.__genarate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串
                if random_str in container.keys():
                    pass
                else:
                    random_str = self.__genarate_random_str()
                    container[random_str] = {}
            self.random_str = random_str # self.random_str = asdfasdfasdfasdf

        container[self.random_str][key] = value
        self.handler.set_cookie("__session__", self.random_str)

    def __getitem__(self, key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        #  专属信息【key】
        random_str =  self.handler.get_cookie("__session__")
        print("client ", random_str)
        if not random_str:
            return None
        # 客户端有随机字符串
        user_info_dict = container.get(random_str,None)
        if not user_info_dict:
            return None
        value = user_info_dict.get(key, None)
        return value

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
            s = Session(self) #将 Indexhandler 类实例化对象传入
            s["is_login"] = True
            s["name"] = self.get_argument("u",None)
            print(container)
            self.write("dffff   df cookie设置成功了")
        else:
            self.write("请d d s 登录")

class ManaggerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        print(container)
        # print(self.get_cookie("__session__"))
        s = Session(self)
        val = s["is_login"]
        if val:
            self.write(s["name"]+"登录成功")
        else:
            self.write("登录失败1sdfdsfds2")



settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"/statics/",
    "cookie_secret":"dsfsfsafsdfasdf",
}

application = tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManaggerHandler),

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py 这次设置cookie获取cookie是 obj【key】 = value

 

需要了解

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法

obj = class1()
obj() ====对象加括号调用的是类的__call__方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
   
class Foo(object):
   
    def __getitem__(self, key):
        print  '__getitem__',key
   
    def __setitem__(self, key, value):
        print '__setitem__',key,value
   
    def __delitem__(self, key):
        print '__delitem__',key
   
   
   
obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

  

2、Tornado扩展

Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作...

class BaseHandler(tornado.web.RequestHandler):
   
    def initialize(self):
        self.xxoo = "wupeiqi"
   
   
class MainHandler(BaseHandler):
   
    def get(self):
        print(self.xxoo)
        self.write('index')
 
class IndexHandler(BaseHandler):
   
    def get(self):
        print(self.xxoo)
        self.write('index')

  

3、session

session其实就是定义在服务器端用于保存用户回话的容器,其必须依赖cookie才能实现。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
  
import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time
  
session_container = {}
  
create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
  
  
class Session(object):
  
    session_id = "__sessionId__"
  
    def __init__(self, request):
        session_value = request.get_cookie(Session.session_id)
        if not session_value:
            self._id = create_session_id()
        else:
            self._id = session_value
        request.set_cookie(Session.session_id, self._id)
  
    def __getitem__(self, key):
        return session_container[self._id][key]
  
    def __setitem__(self, key, value):
        if session_container.has_key(self._id):
            session_container[self._id][key] = value
        else:
            session_container[self._id] = {key: value}
  
    def __delitem__(self, key):
        del session_container[self._id][key]
  
  
class BaseHandler(tornado.web.RequestHandler):
  
    def initialize(self):
        # my_session['k1']访问 __getitem__ 方法
        self.my_session = Session(self)
  
  
class MainHandler(BaseHandler):
  
    def get(self):
        print self.my_session['c_user']
        print self.my_session['c_card']
        self.write('index')
  
class LoginHandler(BaseHandler):
  
    def get(self):
        self.render('login.html', **{'status': ''})
  
    def post(self, *args, **kwargs):
  
        username = self.get_argument('name')
        password = self.get_argument('pwd')
        if username == 'wupeiqi' and password == '123':
  
            self.my_session['c_user'] = 'wupeiqi'
            self.my_session['c_card'] = '12312312309823012'
  
            self.redirect('/index')
        else:
            self.render('login.html', **{'status': '用户名或密码错误'})
  
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
    'login_url': '/login'
}
  
application = tornado.web.Application([
    (r"/index", MainHandler),
    (r"/login", LoginHandler),
], **settings)
  
  
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
自定义Session

 

2 随机验证码图片 

验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。

安装图像处理模块:

pip3 install pillow

  

 

需要一个字体文件 Monaco.ttf

登陆注册的时候,需要验证码的功能,原理为在后台自动创建一张随机图片,然后通过img标签输出到前端。这里我们需要安装一个pillow的模块,相应的生成随机验证代码文件如下,此外还需要一个字体文件

 

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

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

# 路径拼接
# import os
# path = os.path.join(os.path.dirname(__file__),"Monaco.ttf")


def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="Monaco.ttf",
                         # font_type=path,
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance = 2):
    '''
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    '''

    width, height = size # 宽, 高
    img = Image.new(mode, size, bg_color) # 创建图形
    draw = ImageDraw.Draw(img) # 创建画笔

    def get_chars():
        '''生成给定长度的字符串,返回列表格式'''
        return random.sample(chars, length)

    def create_lines():
        '''绘制干扰线'''
        line_num = random.randint(*n_line) # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            #结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        '''绘制干扰点'''
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        '''绘制验证码字符'''
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                    strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)

    return img, strs
check_code.py

注意 check_code文件内的字体MONACO.TTF文件路径在tornado访问注意路径拼接访问

如下这种格式

# 路径拼接
# import os
# path = os.path.join(os.path.dirname(__file__),"Monaco.ttf")


def create_validate_code(size=(120, 30),
。。。。。
                         font_type=path,
。。。。

 

首先在登录界面的 验证码的img标签的src配置一个路由uri   即 <img src="/check_code">   定义了一个事件,点击时候,获取图片src 并个这个标签的src加问号,让图片的src刷新,相当于发送get请求/check_code url这样

后端的的handler对应的checkcode的类get方法就重新生成img二进制数据self.write给这个uri

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <p><input name="user" type="text" placeholder="用户"/></p>
        <p><input name='pwd' type="text" placeholder="密码"/></p>
        <p>
            <input name="code" type="text" placeholder="验证码"/>
            <img src="/check_code" onclick='ChangeCode();' id='imgCode'>
        </p>
        <input type="submit" value="Submit" /> <span style="color:red">{{status}}</span>
    </form>
    <script type="text/javascript">

        function ChangeCode() {
            var code = document.getElementById('imgCode');
            code.src += '?';
        }
    </script>
</body>
</html>
login.html
	<script type="text/javascript">

	    function ChangeCode() {
            var code = document.getElementById('imgCode');
            code.src += '?';
        }
    </script>

index.html 跟上面一样,这里贴一下吧

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <h1>
        cookie有两种写入方式:
        <br>
            1 服务端写入到浏览器
        <br>
            2 浏览器自己通过js写入  document.cookie
    </h1>


</div>

<script src="/statics/jquery.cookie.js"></script>
<script src="/statics/jquery-1.12.4.js"></script>

<script>
    // javascripts方式 在浏览器设置cookie
    function setCookieBySeconds(name,value,expires) {
        var current_date = new Date();
        current_date.setDate(current_date.getSeconds() + expires);
        document.cookie = name + "= " +value +";expires=" + current_date.toUTCString()

    };
    function setCookieByDays(name,value,expires) {

        var current_date = new Date();
        current_date.setDate(current_date.getDay() + expires);
        document.cookie = name +"= " +value +";expires="+current_date.toUTCString() // 要转换为utc时间

    }
    // 调用
    // setCookieBySeconds('k1','v1',5) // 5秒过期
    // setCookieByDays('k1','v1',5) // 5天过期

    //###################jQuery设置###################
    $.cookie('k1','v1',{expires:7}); // 7天 设置k1=v1的cookie7天过期

    //如果不是按天的话,需要先定义时间秒变量
    var  current_date = new Date();
    current_date.setDate(current_date.getSeconds() + 6) ; //当前时间+6s
    $.cookie('k1',"v1",{expires:current_date}) //6s 后过期


</script>

</body>
</html>
index.html

 

接下来是后端的处理程序了

# 图片验证码

class CheckCodeHandler(BaseHandler):
    def get(self, *args, **kwargs):
        #生成 图片并且返回
        import io
        import check_code
        mstream = io.BytesIO()
        img, code = check_code.create_validate_code()

        # 将图片对象写入mstream,
        img.save(mstream,"GIF")
        # 为每个用户保存期验证码
        self.session["CheckCode"] = code

        self.write(mstream.getvalue())
CheckCodeHandler 类
#!/usr/bin/env python
#_*_coding:utf-8_*_

import  tornado.web
import tornado.ioloop

"""
session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法


obj = class1()
obj() ====对象加括号调用的是类的__call__方法

如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法

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

class Foo(object):

    def __getitem__(self, key):
        print  '__getitem__',key

    def __setitem__(self, key, value):
        print '__setitem__',key,value

    def __delitem__(self, key):
        print '__delitem__',key



obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

"""
container = {}
# container = {
#     # "第一个人的随机字符串":{},
#     # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
# }

class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __genarate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        random_str = obj.hexdigest()
        return random_str

    # def set_value(self, key,value):
    def __setitem__(self, key, value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了
            random_str = self.handler.get_cookie('__session__')
            if not random_str:
                random_str = self.__genarate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串
                if random_str in container.keys():
                    pass
                else:
                    random_str = self.__genarate_random_str()
                    container[random_str] = {}
            self.random_str = random_str # self.random_str = asdfasdfasdfasdf

        container[self.random_str][key] = value
        self.handler.set_cookie("__session__", self.random_str)

    def __getitem__(self, key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        #  专属信息【key】
        random_str =  self.handler.get_cookie("__session__")
        print("client ", random_str)
        if not random_str:
            return None
        # 客户端有随机字符串
        user_info_dict = container.get(random_str,None)
        if not user_info_dict:
            return None
        value = user_info_dict.get(key, None)
        return value



class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session = Session(self)#将 Indexhandler 类实例化对象传入




class IndexHandler(BaseHandler):
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
            self.session["is_login"] = True
            self.session["name"] = self.get_argument("u",None)
            print(container)
            self.write("dffff   df cookie设置成功了")
        else:
            self.write("请d d s 登录")

class ManaggerHandler(BaseHandler):
    def get(self, *args, **kwargs):
        print(container)
        # print(self.get_cookie("__session__"))
        val = self.session["is_login"]
        if val:
            self.write(self.session["name"]+"登录成功")
        else:
            self.write("登录失败1sdfdsfds2")


# 图片验证码

class CheckCodeHandler(BaseHandler):
    def get(self, *args, **kwargs):
        #生成 图片并且返回
        import io
        import check_code
        mstream = io.BytesIO()
        img, code = check_code.create_validate_code()

        # 将图片对象写入mstream,
        img.save(mstream,"GIF")
        # 为每个用户保存期验证码
        self.session["CheckCode"] = code

        self.write(mstream.getvalue())


class LoginHandler(BaseHandler):
    def get(self, *args, **kwargs):
        self.render('login.html', status="")

    def post(self, *args, **kwargs):
        user = self.get_argument('user', None)
        pwd = self.get_argument('pwd', None)
        code = self.get_argument('code', None)

        check_code = self.session["CheckCode"]
        if code.upper() == check_code.upper():
            self.write('验证码正确')
        else:
            # self.redirect('/login')
            self.render('login.html', status='验证码错误')


settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"statics",
    "cookie_secret":"dsfsfsafsdfasdf",
}

application = tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManaggerHandler),
    (r"/login", LoginHandler),
    (r"/check_code", CheckCodeHandler),

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py

 

 

3 xss跨站脚本攻击 csrf跨站请求伪造

Xss跨站脚本攻击

  恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的特殊目的。

csrf跨站请求伪造
  get请求的时候,会给浏览器发一个id(cookie),浏览器post请求的时候,携带这个id,然后服务端对其做验证,如果没有这个id的话,就禁止浏览器提交内容。下面来看一下在tornado里面怎么设置,首先需要在settings里面配置 'xsrf_cookies': True,如果这样配置的话,浏览器发送post请求的话这样设置之后,Tornado 将拒绝请求参数中不包含正确的_xsrf 值的 post/put/delete 请求,如果没有携带相应的id(session)则会禁止访问。{% raw xsrf_form_html() %}是新增的,目的就在于实现上面所说的授权给前端以合法请求。

  这里主要看下html里面的代码,用js获取_xsrf对应的session,然后用jquery的ajax发送post请求,并且将_xsrf的session也发送过去。

Tornado中的夸张请求伪造和Django中的相似,跨站伪造请求(Cross-site request forgery)

 1 后端配置

settings = {
    "xsrf_cookies": True,
}
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], **settings)
配置
<form action="/new_message" method="post">
  {% raw xsrf_form_html() %}
  <input type="text" name="message"/>
  <input type="submit" value="Post"/>
</form>
前端使用时候加 {% raw xsrf_form_html() %}
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 + ")"));
    }});
};

使用 - AJAX
1 使用ajax提交

 

index.html login.html 不变 跟上面的一样

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/csrf" method="post">
        {% raw xsrf_form_html() %}
        <!--输出结果如下-->
        <!--<input type="hidden" name="_xsrf" value="2|1a3c1c71|31ad9ca267d4350e09d426eb66244e5b|1470061982">-->
        <p><input name="user" type="text" placeholder="用户"/></p>
        <p><input name='pwd' type="text" placeholder="密码"/></p>
        <p>
            <input name="code" type="text" placeholder="验证码"/>
            <img src="/check_code" onclick='ChangeCode();' id='imgCode'>
        </p>
        <input type="submit" value="Submit" /> <span style="color:red"></span>
    </form>
    <script src="/statics/jquery-1.12.4.js"></script>
    <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" />
    <script type="text/javascript">

        function ChangeCode() {
            var code = document.getElementById('imgCode');
            code.src += '?';
        }
        function getCookie(name) {
            var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");


            return r ? r[1] : undefined;

            /*document.cookie
            "k1=2|1:0|10:1469588097|2:k1|4:OTk5|1b6ea1b4be4fa44f24e1add4447c74c27f969248bef14f19b347ed9f703b20a5; user="2|1:0|10:1469606426|4:user|8:YWxleA==|465b3e5dc3a2585bd5f5200a46c11e406b759a4c4fd7d96f5554b99bcdc619e9"; _xsrf=2|53738789|78e2075a2e9baef6409bbd132f6bd5a3|1470061982; __session__=f01a331979313ee036accf4be458ef11"
            name = "_xsrf"
            "_xsrf"
            document.cookie.match("\\b" + name + "=([^;]*)\\b");
            ["_xsrf=2|53738789|78e2075a2e9baef6409bbd132f6bd5a3|1470061982", "2|53738789|78e2075a2e9baef6409bbd132f6bd5a3|1470061982"]*/
        }

        function SubmitCsrf() {
            var nid = getCookie('_xsrf'); //csrf的name名
            $.post({
                url: '/csrf',
                data: {'k1': 'v1',"_xsrf": nid},
                success: function (callback) {
                    // Ajax请求发送成功有,自动执行
                    // callback,服务器write的数据 callback=“csrf.post”
                    console.log(callback);
                }
            });
        }
    </script>
</body>
</html>
csrf.html

 

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

import  tornado.web
import tornado.ioloop

"""
session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法


obj = class1()
obj() ====对象加括号调用的是类的__call__方法

如果要设置 获取 删除 需要__getitem__ 、__delitem__、__setitem__方法

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

class Foo(object):

    def __getitem__(self, key):
        print  '__getitem__',key

    def __setitem__(self, key, value):
        print '__setitem__',key,value

    def __delitem__(self, key):
        print '__delitem__',key



obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

"""
container = {}
# container = {
#     # "第一个人的随机字符串":{},
#     # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
# }

class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __genarate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time()), encoding='utf-8'))
        random_str = obj.hexdigest()
        return random_str

    # def set_value(self, key,value):
    def __setitem__(self, key, value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str: # 重点1个: 如果我设置1成cookie的key val 这段代码一定会走,但是如果我连续对象调这个方法设置多个值,这段代码则不会走了
            random_str = self.handler.get_cookie('__session__')
            if not random_str:
                random_str = self.__genarate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串
                if random_str in container.keys():
                    pass
                else:
                    random_str = self.__genarate_random_str()
                    container[random_str] = {}
            self.random_str = random_str # self.random_str = asdfasdfasdfasdf

        container[self.random_str][key] = value
        self.handler.set_cookie("__session__", self.random_str)

    def __getitem__(self, key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        #  专属信息【key】
        random_str =  self.handler.get_cookie("__session__")
        print("client ", random_str)
        if not random_str:
            return None
        # 客户端有随机字符串
        user_info_dict = container.get(random_str,None)
        if not user_info_dict:
            return None
        value = user_info_dict.get(key, None)
        return value



class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session = Session(self)#将 Indexhandler 类实例化对象传入




class IndexHandler(BaseHandler):
    def get(self):
        if self.get_argument("u",None) in ["alex","eric"]:
            self.session["is_login"] = True
            self.session["name"] = self.get_argument("u",None)
            print(container)
            self.write("dffff   df cookie设置成功了")
        else:
            self.write("请d d s 登录")

class ManaggerHandler(BaseHandler):
    def get(self, *args, **kwargs):
        print(container)
        # print(self.get_cookie("__session__"))
        val = self.session["is_login"]
        if val:
            self.write(self.session["name"]+"登录成功")
        else:
            self.write("登录失败1sdfdsfds2")


# 图片验证码

class CheckCodeHandler(BaseHandler):
    def get(self, *args, **kwargs):
        #生成 图片并且返回
        import io
        import check_code
        mstream = io.BytesIO()
        img, code = check_code.create_validate_code()

        # 将图片对象写入mstream,
        img.save(mstream,"GIF")
        # 为每个用户保存期验证码
        self.session["CheckCode"] = code

        self.write(mstream.getvalue())


class LoginHandler(BaseHandler):
    def get(self, *args, **kwargs):
        self.render('login.html', status="")

    def post(self, *args, **kwargs):
        user = self.get_argument('user', None)
        pwd = self.get_argument('pwd', None)
        code = self.get_argument('code', None)

        check_code = self.session["CheckCode"]
        if code.upper() == check_code.upper():
            self.write('验证码正确')
        else:
            # self.redirect('/login')
            self.render('login.html', status='验证码错误')

# csrf

class CsrfHandler(BaseHandler):

    def get(self, *args, **kwargs):
        self.render('csrf.html')

    def post(self, *args, **kwargs):
        self.write('csrf.post')


settings = {
    "template_path":"views",
    "static_path":"statics",
    "static_url_prefix":"/statics/",
    "cookie_secret":"dsfsfsafsdfasdf",
    'xsrf_cookies': True # csrf的cookie

}

application = tornado.web.Application([
    (r"/index",IndexHandler),
    (r"/manager",ManaggerHandler),
    (r"/login", LoginHandler),
    (r"/check_code", CheckCodeHandler),
    (r"/csrf", CsrfHandler), # 定义路由

],**settings)

if __name__ == '__main__':

    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
app.py

 

注:Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求

 

 

 

 

文件上传的方式

1、Form表单上传

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>上传文件</title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <input name="fff" id="my_file"  type="file" />
        <input type="submit" value="提交"  />
    </form>
</body>
</html>
HTML
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):

        self.render('index.html')

    def post(self, *args, **kwargs):
        file_metas = self.request.files["fff"]
        # print(file_metas)
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])

settings = {
    'template_path': 'template',
}

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


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

 

2、AJAX上传

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" />
    <script>
        function UploadFile(){
            var fileObj = document.getElementById("img").files[0];

            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", fileObj);

            var xhr = new XMLHttpRequest();
            xhr.open("post", '/index', true);
            xhr.send(form);
        }
    </script>
</body>
</html>

HTML - XMLHttpRequest
XMLHttpRequest
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="file" id="img" />
    <input type="button" onclick="UploadFile();" />
    <script>
        function UploadFile(){
            var fileObj = $("#img")[0].files[0];
            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", fileObj);

            $.ajax({
                type:'POST',
                url: '/index',
                data: form,
                processData: false,  // tell jQuery not to process the data
                contentType: false,  // tell jQuery not to set contentType
                success: function(arg){
                    console.log(arg);
                }
            })
        }
    </script>
</body>
</html>
HTML - jQuery
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
        <div id="main">
            <input name="fff" id="my_file"  type="file" />
            <input type="button" name="action" value="Upload" onclick="redirect()"/>
            <iframe id='my_iframe' name='my_iframe' src=""  class="hide"></iframe>
        </div>
    </form>

    <script>
        function redirect(){
            document.getElementById('my_iframe').onload = Testt;
            document.getElementById('my_form').target = 'my_iframe';
            document.getElementById('my_form').submit();

        }
        
        function Testt(ths){
            var t = $("#my_iframe").contents().find("body").text();
            console.log(t);
        }
    </script>
</body>
</html>
HTML - iframe
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):

        self.render('index.html')

    def post(self, *args, **kwargs):
        file_metas = self.request.files["fff"]
        # print(file_metas)
        for meta in file_metas:
            file_name = meta['filename']
            with open(file_name,'wb') as up:
                up.write(meta['body'])

settings = {
    'template_path': 'template',
}

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


if __name__ == "__main__":
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()
Python
<script type="text/javascript">
 
    $(document).ready(function () {
 
        $("#formsubmit").click(function () {
 
            var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');
 
            $("body").append(iframe);
 
            var form = $('#theuploadform');
            form.attr("action", "/upload.aspx");
            form.attr("method", "post");
 
            form.attr("encoding", "multipart/form-data");
            form.attr("enctype", "multipart/form-data");
 
            form.attr("target", "postiframe");
            form.attr("file", $('#userfile').val());
            form.submit();
 
            $("#postiframe").load(function () {
                iframeContents = this.contentWindow.document.body.innerHTML;
                $("#textarea").html(iframeContents);
            });
 
            return false;
 
        });
 
    });
 
</script>
 
 
<form id="theuploadform">
    <input id="userfile" name="userfile" size="50" type="file" />
    <input id="formsubmit" type="submit" value="Send File" />
</form>
 
<div id="textarea">
</div>
扩展:基于iframe实现Ajax上传示例

 

posted @ 2016-08-02 00:49  众里寻,阑珊处  阅读(323)  评论(0编辑  收藏  举报
返回顶部