tornado 框架二(cookie session ajax 表单上传)

1.cookie 是在浏览器端保存的键值对 ,特性:每次http请求携带cookie

  tornado 在后台设置
    self.cookies
    self.set_cookie('k','v',过期时间)
    self.get_cookie('')


  在前台javascript设置
    document.cookie
    document.cookie['k']
    document.cookie.split(';')
    document.cookie = "k2=v2;path=/;"


  自定义函数 过期时间要转换为UTC时间
    function setCookie(name,value,expires){
      var current_date = new Date();
      current_date.setSecondes(current_date.getSeconds()+5);
      document.cookie = name + "=" + value + ";expires=" + current_date.toUTCString();
    }

 

  jquery 设置cookie
  下载jquery cookie 插件
  $.cookie('k','v',{path:'/',domin:'',expires:})

 2.cookie 安全验证

  使用

    self.set_secure_cookie()

    self.get_secure_cookie()

  setting 里面设置

    'cookie_secret':'abcdefg'

3.签名的Cookie流程:

  • 写cookie过程:
  1. 将值进行base64加密
  2. 对除值以外的内容进行签名,哈希算法(无法逆向解析)
  3. 拼接 签名 + 加密值
  • 读cookie过程:
  1. 读取 签名 + 加密值
  2. 对签名进行验证
  3. base64解密,获取值内容

   k1= v1 

     v1 =base64(v1)

   v1|v1+当前时间戳 +自定义字符串

 k1 = v1|加密字符串|当前时间戳

 

4.自定义 session  加密签名实例

#!/usr/bin/env python
# --*-- encoding:utf-8 --*--
import tornado.web
import  tornado.ioloop
import hashlib
import  time

contain = {}
class Session:
    def __init__(self,handler):
        self.handler = handler  # 传递过来的handler 用于获取cookie
        self.random_str = None # 防止多次设置,产生多个随机字符串

    def __get_random_str(self):
        # 获取随机的字符串
        hmd5 = hashlib.md5()
        hmd5.update(bytes(str(time.time()),encoding='utf-8'))
        random_str = hmd5.hexdigest()
        return random_str

    def set_value(self,key,value):
        if not self.random_str: # 如果 第一次 set_value
            random_str = self.handler.get_cookie('abcdefg') #获取cookie
            if not random_str:  #如果客户端没有 cookie
                random_str = self.__get_random_str()  # 生成随机字符串
                contain[random_str] = {} #创建一个以随机字符串为key 的 值为空的字典
            else:  #如果客户端有 cookie  ,而服务器没有,服务器重启
                if random_str in contain.keys():
                    pass
                else:#服务器没有随机字符串
                    random_str = self.__get_random_str()
                    contain[random_str] = {}
            self.random_str = random_str
        #设置当前用户的属性  必须使用 self.random_str ,使用 random_str 第二次 set_value时,会出现举报变量的错误
        contain[self.random_str][key]=value
        #设置cookie
        self.handler.set_secure_cookie('abcdefg',self.random_str)

    def get_value(self,key):
        #获取客户端cookie
        random_str = self.handler.get_secure_cookie('abcdefg')
        # get_secure_cookie  获得是字节码,需要装换
        random_str = str(random_str,encoding='utf-8')
        if not random_str: # 客户端没有cookie
            return None

        current_user_info = contain.get(random_str,None)  #获取服务端当前用户的属性
        if not current_user_info: # 服务端当前用户没有设置属性
            return  None

        value = current_user_info.get(key,None) # 获取当前用户key 对应的值
        return  value




class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        if self.get_argument('username',None) in ['abc','bcd']:
            s = Session(self) # 传递 handler
            s.set_value('is_login',True)
            s.set_value('username',self.get_argument('username'))
        else:
            self.write('请登录')

class ManageHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        s = Session(self)
        val = s.get_value('is_login')
        if val:
            self.write('成功'+s.get_value('username'))
        else:
            self.write('失败')


setting = {
    'static_path':'static',
    'template_path':'view',
    'cookie_secret':'123456'
}

application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manage',ManageHandler),
],**setting)


if __name__ == '__main__':
    application.listen('8080')
    tornado.ioloop.IOLoop.instance().start()
View Code

 自定义 session 修订:

#!/usr/bin/env python
# --*-- encoding:utf-8 --*--
import tornado.web
import  tornado.ioloop
import hashlib
import  time

contain = {}
class Session:
    def __init__(self,handler):
        self.handler = handler  # 传递过来的handler 用于获取cookie
        self.random_str = None # 防止多次设置,产生多个随机字符串

    def __get_random_str(self):
        # 获取随机的字符串
        hmd5 = hashlib.md5()
        hmd5.update(bytes(str(time.time()),encoding='utf-8'))
        random_str = hmd5.hexdigest()
        return random_str

    def __setitem__(self,key,value): 
        if not self.random_str: # 如果 第一次 set_value
            random_str = self.handler.get_cookie('abcdefg') #获取cookie
            if not random_str:  #如果客户端没有 cookie
                random_str = self.__get_random_str()  # 生成随机字符串
                contain[random_str] = {} #创建一个以随机字符串为key 的 值为空的字典
            else:  #如果客户端有 cookie  ,而服务器没有,服务器重启
                if random_str in contain.keys():
                    pass
                else:#服务器没有随机字符串
                    random_str = self.__get_random_str()
                    contain[random_str] = {}
            self.random_str = random_str
        #设置当前用户的属性  必须使用 self.random_str ,使用 random_str 第二次 set_value时,会出现举报变量的错误
        contain[self.random_str][key]=value
        #设置cookie
        self.handler.set_secure_cookie('abcdefg',self.random_str)

    def __getitem__(self,key):
        #获取客户端cookie
        random_str = self.handler.get_secure_cookie('abcdefg')
        # get_secure_cookie  获得是字节码,需要装换
        random_str = str(random_str,encoding='utf-8')
        if not random_str: # 客户端没有cookie
            return None

        current_user_info = contain.get(random_str,None)  #获取服务端当前用户的属性
        if not current_user_info: # 服务端当前用户没有设置属性
            return  None

        value = current_user_info.get(key,None) # 获取当前用户key 对应的值
        return  value

class BaseHandler(tornado.web.RequestHandler):
    #钩子函数,get post 方法之前 都会执行此方法
    def initialize(self):
        self.session = Session(self) # 传递 handler


class IndexHandler(BaseHandler):
    def get(self, *args, **kwargs):
        if self.get_argument('username',None) in ['abc','bcd']:
            self.session['is_login']=True
            self.session['username']=self.get_argument('username')
        else:
            self.write('请登录')

class ManageHandler(BaseHandler):
    def get(self, *args, **kwargs):
        val = self.session['is_login']
        if val:
            self.write('成功'+self.session['username'])
        else:
            self.write('失败')


setting = {
    'static_path':'static',
    'template_path':'view',
    'cookie_secret':'123456'
}

application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manage',ManageHandler),
],**setting)


if __name__ == '__main__':
    application.listen('8080')
    tornado.ioloop.IOLoop.instance().start()
View Code

 

5.基于 session 的图片验证码

1. 先安装图像处理模块 pip3 install pillow  2.导入源码check_code.py  Monaco.ttf 两个文件   

启动文件:run.py

#!/usr/bin/env python
# --*-- encoding:utf-8 --*--
import tornado.web
import  tornado.ioloop
import hashlib
import  time
import io
import check_code

contain = {}
class Session:
    def __init__(self,handler):
        self.handler = handler  # 传递过来的handler 用于获取cookie
        self.random_str = None # 防止多次设置,产生多个随机字符串

    def __get_random_str(self):
        # 获取随机的字符串
        hmd5 = hashlib.md5()
        hmd5.update(bytes(str(time.time()),encoding='utf-8'))
        random_str = hmd5.hexdigest()
        return random_str

    def __setitem__(self,key,value):
        if not self.random_str: # 如果 第一次 set_value
            random_str = self.handler.get_cookie('abcdefg') #获取cookie
            if not random_str:  #如果客户端没有 cookie
                random_str = self.__get_random_str()  # 生成随机字符串
                contain[random_str] = {} #创建一个以随机字符串为key 的 值为空的字典
            else:  #如果客户端有 cookie  ,而服务器没有,服务器重启
                if random_str in contain.keys():
                    pass
                else:#服务器没有随机字符串
                    random_str = self.__get_random_str()
                    contain[random_str] = {}
            self.random_str = random_str
        #设置当前用户的属性  必须使用 self.random_str ,使用 random_str 第二次 set_value时,会出现举报变量的错误
        contain[self.random_str][key]=value
        #设置cookie
        self.handler.set_secure_cookie('abcdefg',self.random_str)

    def __getitem__(self,key):
        #获取客户端cookie
        random_str = self.handler.get_secure_cookie('abcdefg')
        # get_secure_cookie  获得是字节码,需要装换
        random_str = str(random_str,encoding='utf-8')
        if not random_str: # 客户端没有cookie
            return None

        current_user_info = contain.get(random_str,None)  #获取服务端当前用户的属性
        if not current_user_info: # 服务端当前用户没有设置属性
            return  None

        value = current_user_info.get(key,None) # 获取当前用户key 对应的值
        return  value

class BaseHandler(tornado.web.RequestHandler):
    #钩子函数,get post 方法之前 都会执行此方法
    def initialize(self):
        self.session = Session(self) # 传递 handler


class IndexHandler(BaseHandler):
    def get(self, *args, **kwargs):
        if self.get_argument('username',None) in ['abc','bcd']:
            self.session['is_login']=True
            self.session['username']=self.get_argument('username')
        else:
            self.write('请登录')


class ManageHandler(BaseHandler):
    def get(self, *args, **kwargs):
        val = self.session['is_login']
        if val:
            self.write('成功'+self.session['username'])
        else:
            self.write('失败')

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

    def post(self, *args, **kwargs):
        text_code = self.get_argument('code')
        img_code = self.session["CheckCode"]
        if text_code.upper() == img_code.upper():
            self.write('验证码正确')
        else:
            self.render('login.html', state='验证码失败')

class CheckCodeHandler(BaseHandler):
    def get(self, *args, **kwargs):
        mstream = io.BytesIO()
        #创建图片,写入验证码
        img, code = check_code.create_validate_code()
        #将图片对象写入 mstream
        img.save(mstream, "GIF")
        self.session["CheckCode"] = code
        self.write(mstream.getvalue())

setting = {
    'static_path':'static',
    'template_path':'view',
    'cookie_secret':'123456'
}

application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manage',ManageHandler),
    (r'/login',LoginHandler),
    (r'/checkcode',CheckCodeHandler),
],**setting)


if __name__ == '__main__':
    application.listen('8080')
    tornado.ioloop.IOLoop.instance().start()
View Code

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <p><input type="text" placeholder="用户名"></p>
        <p><input type="text" placeholder="密码"></p>
        <p><input type="text" placeholder="验证码" name="code"><img id="imgCode" src="/checkcode" onclick="ChangeCode();"><span style="color: red">{{state}}</span></p>
        <p><input type="submit" value="提交"></p>
    </form>
    <script>
        // 点击图片 改变验证码
        function ChangeCode() {
            var code = document.getElementById('imgCode');
            code.src +='?';
        }
    </script>
</body>
</html>
View Code

 

6.CSRF 跨站请求伪造

配置使用

class CsrfHandler(BaseHandler):
    def get(self, *args, **kwargs):
        self.render('csrf.html')
    def post(self, *args, **kwargs):
        self.write('csrf.post')

setting = {
    'static_path':'static',
    'template_path':'view',
    'cookie_secret':'123456',
    'xsrf_cookies':True,
}

application = tornado.web.Application([
    (r'/index',IndexHandler),
    (r'/manage',ManageHandler),
    (r'/login',LoginHandler),
    (r'/checkcode',CheckCodeHandler),
    (r'/csrf',CsrfHandler),
],**setting)
    <form action="/csrf" method="post">
        {% raw xsrf_form_html()%}
        <p><input type="submit" value="提交"></p>
    </form>

 基于ajax 的跨站请求伪造

<body>
    <!--<form action="/csrf" method="post">-->
        <!--{% raw xsrf_form_html()%}-->
        <!--<p><input type="submit" value="提交"></p>-->
    <!--</form>-->

    <p><input type="button" value="提交" onclick="ajaxCsrf();"></p>
    <script>
        function getCookie(name) {
            var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
            return r ? r[1] : undefined ;
        }
        function ajaxCsrf() {
            var nid = getCookie("_xsrf");
            $.post({
                url:'/csrf',
                data:{'k1':'v1','_xsrf':nid},
                success:function (callback) {
                    console.log(callback);
                }
            });
        }
    </script>

 7.ajax  异步+javascript+html ,异步+javascript+json

  偷偷的完成局部页面的刷新

  1)利用iframe实现 伪ajax

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <p> 请输入要加载的地址: <span id="currentTime"></span></p>
        <p>
            <input id="url" type="text">
            <input type="button" value="加载" onclick="loadPage();">
        </p>
    </div>
    <div>
        <iframe id="iframePosition" style="width: 500px;height: 500px"></iframe>
    </div>
    <script>
        window.onload= function () {
            var date = new Date();
            document.getElementById('currentTime').innerText = date.getTime();
        }
        function loadPage() {
            var url = document.getElementById('url').value;
            document.getElementById('iframePosition').src = url;
        }
    </script>
</body>
</html>
View Code

  2)原生的ajax,get方式传递值在URL后面+?+data ,post方式传递值 send(data)

javascript:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div> <input type="button" value="get/ajax" onclick="GetAjax();"></div>
    <div> <input type="button" value="post/ajax" onclick="PostAjax();"></div>
    <script>
        function GetXHR() {
            var xhr = null;
            if(XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else {
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xhr
        }
        function GetAjax() {
            var data = 'a=123;b=456';
            // 创建可兼容的xhr对象
            var xhr = GetXHR();
            // 设置回调函数
            xhr.onreadystatechange = function () {
                if(xhr.readyState==4){ //收到全部响应数据,执行以下操作
                    console.log(xhr.responseText)
                }
            }
            //指定连接方式,地址等
            xhr.open('get','/testajax'+'?'+data,true); // get 方式传递数据
            //发送请求
            xhr.send();
        }
        function PostAjax() {
            var data = 'a=123;b=456';
            // 创建可兼容的xhr对象
            var xhr = GetXHR();
            // 设置回调函数
            xhr.onreadystatechange = function () {
                if(xhr.readyState==4){ //收到全部响应数据,执行以下操作
                    console.log(xhr.responseText)
                }
            }
            //指定连接方式,地址等
            xhr.open('post','/testajax',true);
            //设置请求头
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
            //发送请求
            xhr.send(data); // post方式传递数据
        }
    </script>
</body>
</html>
View Code

run.py

class TestAjaxHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        print(self.get_argument('a'),self.get_argument('b'))
        self.render('rawajax.html')
        # self.write('hell0')

    def post(self, *args, **kwargs):
        print(self.get_argument('a'), self.get_argument('b'))
        self.write('hello')
View Code

  3)jquery ajax

  常用的方法

jQuery.get(...)
                所有参数:
                     url: 待载入页面的URL地址
                    data: 待发送 Key/value 参数。
                 success: 载入成功时回调函数。
                dataType: 返回内容格式,xml, json,  script, text, html


            jQuery.post(...)
                所有参数:
                     url: 待载入页面的URL地址
                    data: 待发送 Key/value 参数
                 success: 载入成功时回调函数
                dataType: 返回内容格式,xml, json,  script, text, html


            jQuery.getJSON(...)
                所有参数:
                     url: 待载入页面的URL地址
                    data: 待发送 Key/value 参数。
                 success: 载入成功时回调函数。


            jQuery.getScript(...)
                所有参数:
                     url: 待载入页面的URL地址
                    data: 待发送 Key/value 参数。
                 success: 载入成功时回调函数。


            jQuery.ajax(...)

                部分参数:

                        url:请求地址
                       type:请求方式,GET、POST(1.9.0之后用method)
                    headers:请求头
                       data:要发送的数据
                contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
                      async:是否异步
                    timeout:设置请求超时时间(毫秒)

                 beforeSend:发送请求前执行的函数(全局)
                   complete:完成之后执行的回调函数(全局)
                    success:成功之后执行的回调函数(全局)
                      error:失败之后执行的回调函数(全局)
                

                    accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型
                   dataType:将服务器端返回的数据转换成指定类型
                                   "xml": 将服务器端返回的内容转换成xml格式
                                  "text": 将服务器端返回的内容转换成普通文本格式
                                  "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
                                "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
                                  "json": 将服务器端返回的内容转换成相应的JavaScript对象
                                 "jsonp": JSONP 格式
                                          使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数

                                  如果不指定,jQuery 将自动根据HTTP包MIME信息返回相应类型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string

                 converters: 转换器,将服务器端的内容根据指定的dataType转换类型,并传值给success回调函数
                         $.ajax({
                              accepts: {
                                mycustomtype: 'application/x-some-custom-type'
                              },
                              
                              // Expect a `mycustomtype` back from server
                              dataType: 'mycustomtype'

                              // Instructions for how to deserialize a `mycustomtype`
                              converters: {
                                'text mycustomtype': function(result) {
                                  // Do Stuff
                                  return newresult;
                                }
                              },
                            });

jQuery Ajax 方法列表
View Code

  使用实例

class TestAjaxHandler(tornado.web.RequestHandler):

    def post(self, *args, **kwargs):
        print(self.get_argument('a'), self.get_argument('b'))
        data = '{"hello":"sb"}'  # 内双外单
        self.write(data)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{{static_url('jquery-3.2.1.js')}}"></script>
</head>
<body>
    <div><input type="button" value="jquery/ajax" onclick="JqueryAjax();"></div>
    <script>
        function JqueryAjax() {
            $.ajax({
                url:'/testajax',
                type:'post',
                data: 'a=123;b=456',
                dataType:'json',
                success:function (data) {
                    console.log(data);
                }
            })
        }
    </script>
</body>
</html>

   4)form表单文件上传,多选框

run.py

file_list = []
class FormHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('form.html',FileList = file_list)
    def post(self, *args, **kwargs):
        print(self.get_arguments('aaa')) # checkbox 使用 get_arguments
        #上传文件处理
        file_metas = self.request.files["bbb"] # 文件信息字典
        file_path ='static/file'
        for meta in file_metas:
            file_name = meta['filename']
            file_list.append(file_name)
            with open(os.path.join(file_path,file_name), 'wb') as up:
                up.write(meta['body'])
        self.redirect('/form')
View Code

form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <ul>
            {% for i in FileList%}
                <li><img style="width: 50px;height: 50px" src="static/file/{{i}}"></li>
            {% end %} 
        </ul>
    </div>
    <form action="/form" method="post" enctype="multipart/form-data">
        <p>
            <input type="checkbox" name="aaa" value="1"><input type="checkbox" name="aaa" value="2"><input type="checkbox" name="aaa" value="3"></p>
        <p><input type="file" name="bbb"></p>
        <p><input type="submit" value="提交"></p>
    </form>
</body>
</html>
View Code

ajax 实现文件上传,后台代码不变

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <input type="file" id="f1">
        <input type="button" value="ajax提交" onclick="ajax_file_up();">
        <input type="button" value="jquery ajax提交" onclick="jquery_ajax_file_up();">
    </div>
    <script src="{{static_url('jquery-3.2.1.js')}}"></script>
     <script>
         // 原生的ajax
         function ajax_file_up() {
             file_obj = document.getElementById('f1').files[0];
             var form = new  FormData();
             form.append('aaa',1);
             form.append('aaa',2);
             form.append('bbb',file_obj);
             var xhr = new XMLHttpRequest();
             xhr.open('post','/form',true);
             xhr.send(form);
         }
         //jquery ajax
         function jquery_ajax_file_up() {
             file_obj = $('#f1')[0].files[0];
             var form = new  FormData();
             form.append('aaa',1);
             form.append('aaa',2);
             form.append('bbb',file_obj);
             $.ajax({
                 type:'post',
                 url:'/form',
                 data:form,
                 processData: false,  // tell jQuery not to process the data
                 contentType: false,  // tell jQuery not to set contentType
                 success:function (da) {
                     console.log(da);
                 }
             })
         }
     </script>
</body>
</html>
View Code

 iframe 实现上传 (new FormData() IE浏览器不支持时使用)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form id="form" action="/form" method="post" enctype="multipart/form-data">
        <p>
            <input type="checkbox" name="aaa" value="1"><input type="checkbox" name="aaa" value="2"><input type="checkbox" name="aaa" value="3"></p>
        <p><input type="file" name="bbb"></p>
        <p><input type="button" value="上传" onclick="redirect();"></p>
        <p><iframe id="my_iframe" name="myiframe" style="display: none"></iframe></p>
    </form>
    <script src="{{static_url('jquery-3.2.1.js')}}"></script>
    <script>
        function redirect() {
            document.getElementById('my_iframe').onload = handleresponse;
            document.getElementById('form').target = 'myiframe';
            document.getElementById('form').submit();
        }
        function handleresponse() {
            var res = $("#my_iframe").contents().find('body').text();
            console.log(res);
        }
    </script>
</body>
</html>
View Code

 

8.浏览器的同源策略

 支持跨域:

  <script src='www.google.com/xxx.js'>

  <img src='www.baidu.com/xxx.jpg'>

  <iframe src='www.sina.com'>

不支持跨域:

  ajax

  ....

突破的方法一: jsonp实现跨域请求  只能是GET请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p><input type="button" value="jsonp1" onclick="jsonp1();"></p>
    <p><input type="button" value="jsonp2" onclick="jsonp2();"></p>
    <script src="static/jquery-3.2.1.js"></script>
    <script>
        // jsonp 跨域请求的本质
        function jsonp1() {
            //创建 script 标签
            var tag =document.createElement('script');
            //设置 script 标签 src 的属性
            tag.src = 'http://www.baidu.com/XXX';
            // 在文档的头部添加script标签
            document.head.appendChild(tag);
             // 在文档的头部移除script标签
            document.head.removeChild(tag);
        } // 服务器可以返回 字符串,函数
        
        // ajax  jsonp  中使用的的就是上述的方法
         function jsonp2() {
             $.ajax({
                 url:'http://www.baidu.com/XXX',
                 type:'get',
                 dateType:'jsonp',
                 success:function (data, statusText, xmlHttpRequest) {
                     console.log(data);
                 },
                 jsonp:'callback',  //  设置这个属性后,将服务器的函数设置为 func ,没有设置这个属性,jsonpCallBack的函数名与服务器返回函数名一致
                 jsonpCallBack:'func', // 服务器返回一个func 函数  (self.write('func(123)'))
             });
         }
         function func(arg) {
             console.log(arg);
         }
        
    </script>
</body>
</html>
View Code

服务器让别人可以自定义函数进行跨域请求:

  callback =self.get_argument('callback')

  self.write('%s([11,22,33])' % callback)

原生的可以这样设置:   tag.src = 'http://www.baidu.com/XXX?callback=myfunc';

ajax jsonp 可以这样设置:  jsonp:'callback',  jsonpCallBack:'myfunc'

突破方法二:跨域资源共享(CORS,Cross-Origin Resource Sharing)

* 简单请求 OR 非简单请求
条件:
    1、请求方式:HEAD、GET、POST
    2、请求头信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 对应的值是以下三个中的任意一个
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain
 
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

* 简单请求和非简单请求的区别?
  简单请求:一次请求
  非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”   - 请求方式:OPTIONS   - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息   - 如何“预检” => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers

a、支持跨域,简单请求

服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'

客户端:html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="ajax_corf" onclick="AjaxCorf();">
    <script src="static/jquery-3.2.1.js"></script>
    <script>
        function AjaxCorf() {
            $.ajax({
                url:'http://www.server.com:8008/index',
                type:'get',
                success:function (data) {
                    console.log(data);
                }
            });
        }
    </script>
</body>
</html>
View Code

服务端:py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        #设置一个或者多个 允许跨域域名
        self.set_header('Access-Control-Allow-Origin','http://www.client1.com:8001,http://www.client2.com:8001')
        # 所有域名均可访问
        # self.set_header('Access-Control-Allow-Origin', '*')
        self.write('server get')
    def post(self, *args, **kwargs):
        pass

setting = {
    'template_path':'template',
    'static_path':'static',
}

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

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

b、支持跨域,复杂请求

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Allow-Method
  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Allow-Headers
  • “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age

客户端:html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="ajax_corf" onclick="AjaxCorf();">
    <script src="static/jquery-3.2.1.js"></script>
    <script>
        function AjaxCorf() {
            $.ajax({
                url:'http://www.server.com:8008/index',
                type:'put',
                headers:{'k1':'v1'},
                success:function (data) {
                    console.log(data);
                }
            });
        }
    </script>
</body>
</html>
View Code

服务端:py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        #设置一个或者多个 允许跨域域名
        self.set_header('Access-Control-Allow-Origin','http://www.zzy.com:8001')
        # 所有域名均可访问
        # self.set_header('Access-Control-Allow-Origin', '*')
        self.write('server get')
    def post(self, *args, **kwargs):
        pass

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', 'http://www.zzy.com:8001')
        self.set_header('Access-Control-Allow-Methods','PUT,DELETE')  #必须要大写 ,没*的写法
        self.set_header('Access-Control-Allow-Headers', "k1,k2")  # 允许自定义请求头
        self.set_header('Access-Control-Max-Age', 10) #预检超时时间

    def put(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', 'http://www.client.com:8001')
        self.write('server put')


setting = {
    'template_path':'template',
    'static_path':'static',
}

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

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

c、跨域获取响应头

默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。

客户端:html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="ajax_corf" onclick="AjaxCorf();">
    <script src="static/jquery-3.2.1.js"></script>
    <script>
        function AjaxCorf() {
            $.ajax({
                url:'http://www.server.com:8008/index',
                type:'put',
                headers:{'k1':'v1'},
                success:function (data, statusText, xmlHttpRequest) {
                    console.log(data);
                    //获取响应头
                    console.log(xmlHttpRequest.getAllResponseHeaders())
                }
            });
        }
    </script>
</body>
</html>
View Code

服务端:py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        #设置一个或者多个 允许跨域域名
        self.set_header('Access-Control-Allow-Origin','http://www.zzy.com:8001')
        # 所有域名均可访问
        # self.set_header('Access-Control-Allow-Origin', '*')
        self.write('server get')
    def post(self, *args, **kwargs):
        pass

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', 'http://www.client.com:8001')
        self.set_header('Access-Control-Allow-Methods','PUT,DELETE')  #必须要大写 ,没*的写法
        self.set_header('Access-Control-Allow-Headers', "k1,k2")  # 允许自定义请求头
        self.set_header('Access-Control-Max-Age', 10) #预检超时时间

    def put(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', 'http://www.zzy.com:8001')
        # 设置响应头
        self.set_header('sb','12345')
        self.set_header('hb','56789')
        self.set_header('Access-Control-Expose-Headers', "sb,hb")

        self.write('server put')


setting = {
    'template_path':'template',
    'static_path':'static',
}

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

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

d、跨域传输cookie

在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。

如果想要发送:

  • 浏览器端:XMLHttpRequest的withCredentials为true
  • 服务器端:Access-Control-Allow-Credentials为true
  • 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *

客户端:html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="ajax_corf" onclick="AjaxCorf();">
    <script src="static/jquery-3.2.1.js"></script>
    <script>
        function AjaxCorf() {
            $.ajax({
                url:'http://www.server.com:8008/index',
                type:'put',
                headers:{'k1':'v1'},
                xhrFields:{withCredentials: true},
                success:function (data, statusText, xmlHttpRequest) {
                    console.log(data);
                    //获取响应头
                    console.log(xmlHttpRequest.getAllResponseHeaders());
                }
            });
        }
    </script>
</body>
</html>
View Code

服务端:py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        #设置一个或者多个 允许跨域域名
        self.set_header('Access-Control-Allow-Origin','http://www.zzy.com:8001')
        # 所有域名均可访问
        # self.set_header('Access-Control-Allow-Origin', '*')
        self.write('server get')
    def post(self, *args, **kwargs):
        pass

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', 'http://www.zzy.com:8001')
        self.set_header('Access-Control-Allow-Methods','PUT,DELETE')  #必须要大写 ,没*的写法
        self.set_header('Access-Control-Allow-Headers', "k1,k2")  # 允许自定义请求头
        self.set_header('Access-Control-Max-Age', 10) #预检超时时间
        self.set_header('Access-Control-Allow-Credentials', "true")

    def put(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', 'http://www.zzy.com:8001')
        self.set_header('Access-Control-Allow-Credentials', "true")
        print(self.cookies)
        self.set_cookie('abc','def')
        # 设置响应头
        self.set_header('sb','12345')
        self.set_header('hb','56789')
        self.set_header('Access-Control-Expose-Headers', "sb,hb")

        self.write('server put')


setting = {
    'template_path':'template',
    'static_path':'static',
}

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

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

 

posted @ 2017-06-01 07:44  1916  阅读(154)  评论(0)    收藏  举报