跨域 和同源策略

用ajax 不能实现跨域

<script>

     $(".b1").click(function () {
         $.ajax({
             url:"http://127.0.0.1:8002/ajax_send/",/本服务地址是8000浏览器的同源策略的原因,AJax无法发送跨域请求
             success:function (data) {
                 alert(data)
             }
         })
     });
</script>

报错信息如下

 

同源策略与Jsonp

同源策略

 jison和jsonp的区别点击

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。
如果非同源,那么在请求数据时浏览器会在控制台中报一个异常,提示拒绝访问
 

示例:

项目1:               
http://127.0.0.1:8001项目的index
项目中有一个按钮绑定功能
 $.ajax({
            url:"http://127.0.0.1:8002/SendAjax/", 是跨域的
==================================http://127.0.0.1:8001项目的index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>


<button>ajax</button>
{% csrf_token %}

<script>
    $("button").click(function(){


        $.ajax({
            url:"http://127.0.0.1:8002/SendAjax/",
            type:"POST",
            data:{"username":"yuan","csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val()},
            success:function(data){
                alert(123);
                alert(data)
            }
        })
    })
</script>
</body>
</html>


==================================http://127.0.0.1:8001项目的views

def index(request):


    return render(request,"index.html")


def ajax(request):
    import json
    print(request.POST,"+++++++++++")
    return HttpResponse(json.dumps("hello"))
View Code

项目2:

  

==================================http://127.0.0.1:8002项目的index
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>


<button>sendAjax</button>

{% csrf_token %}

<script>
    $("button").click(function(){


        $.ajax({
            url:"/SendAjax/",
            type:"POST",
            data:{"username":"yuan","csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val()},
            success:function(data){
                alert(data)
            }
        })
    })
</script>

</body>
</html>


==================================http://127.0.0.1:8002项目的views

def index(request):

    return render(request,"index.html")

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt


def SendAjax(request):

    import json

    print("++++++++")

    return HttpResponse(json.dumps("hello2"))
View Code

 当点击项目1的按钮时,发送了请求,但是会发现报错如下

已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8002/SendAjax/ 的远程资。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

但是注意,项目2中的访问已经发生了,说明是浏览器非同源请求返回的结果做了拦截

Jsonp

jsonp是json用来跨域的一个东西。原理是通过script标签跨域特性绕过同源策略

思考:这算怎么回事?

<script src="http://code.jquery.com/jquery-latest.js"></script>

在引入网络cdn的js文件也是跨域的请求,但是可以实现, 正式因为script标签有跨域的特性

 借助script标签,实现跨域请求,示例:

# =============================http://127.0.0.1:8001/index


<button>ajax</button>
{% csrf_token %}

<script>
    function func(name){
        alert(name)
    }
</script>

<script src="http://127.0.0.1:8002/SendAjax/"></script>   他得到的就是 func("yuan") 是另一个8002项目返回的,所以就能够执行上边定义的函数
                                                        func() 从而将yuan显示出来
# =============================http://127.0.0.1:8002/ from django.views.decorators.csrf import csrf_exempt @csrf_exempt def SendAjax(request): import json print("++++++++") # dic={"k1":"v1"} return HttpResponse("func('yuan')") # return HttpResponse("func('%s')"%json.dumps(dic)) 将这个结果返回上边发送跨域请求的script

 

这其实就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。

JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义

      一般情况下,我们希望这个script标签能够动态的调用,而不是像上面因为固定在html里面所以没等页面显示就执行了,很不灵活。我们可以通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务了。

 

<button onclick="f()">sendAjax</button>

<script>
    function addScriptTag(src){
         var script = document.createElement('script');
         script.setAttribute("type","text/javascript");
         script.src = src;
         document.body.appendChild(script);
         document.body.removeChild(script);
    }


    function func(name){
        alert("hello"+name)
    }

    function f(){
         addScriptTag("http://127.0.0.1:7766/SendAjax/")
    }
</script>

 

为了更加灵活,现在将你自己在客户端定义的回调函数函数名传送给服务端服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调:

 

将8001的f()改写为:

function f(){
         addScriptTag("http://127.0.0.1:8002/SendAjax/?callbacks=func")
    }

8002的views改为:

def SendAjax(request):
 
    import json
 
    dic={"k1":"v1"}
 
    print("callbacks:",request.GET.get("callbacks"))
    callbacks=request.GET.get("callbacks")
 
    return HttpResponse("%s('%s')"%(callbacks,json.dumps(dic)))

jQuery对JSONP的实现

getJSON

jQuery框架也当然支持JSONP,可以使用$.getJSON(url,[data],[callback])方法

8001的html改为:

<button onclick="f()">sendAjax</button>

<script>

    function f(){
          $.getJSON("http://127.0.0.1:8002/SendAjax/?callbacks=?",function(arg){
            alert("hello"+arg)
        });
    }
    
</script>


# $.getJSON("http://127.0.0.1:8002/SendAjax/?callbacks=?"   这句话的功能就是将引号中的地址内容转换成script的
实际就是转换成成了 <script src="http://....../?callbacks=?"><?script> 这个
function  就是匿名回调函数  arg就是返回的值  

8002的views不改动。  详见上文

结果是一样的,要注意的是在url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号内部自动生成的一个回调函数名

 

      此外,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?我们可以使用$.ajax方法来实现

 $.ajax

8001的html改为:

<script>

    function f(){
          $.ajax({
                url:"http://127.0.0.1:8002/SendAjax/",
                dataType:"jsonp",   #期待数据类型,  写jsonp 就是告诉服务器,  ajax方法就能根据这个jsonp参数   来生成加<script>标签形式发送请求
                jsonp: 'callbacks',
                jsonpCallback:"SayHi"#下边这两个参数就是让上边的url最后形成

            #"http://127.0.0.1:8002/SendAjax/?callbacks=SayHi"这种形式所以还要有一个SayHi函数来接收参数
}); } function SayHi(arg){ #写好的函数,用于将上边的url 最后合成的 <script src="地址"></script> alert(arg); } </script>
               #形成的地址到 另一个服务器后去的数据 来执行这个函数
 

8002的views不改动

当然,最简单的形式还是通过回调函数来处理:*****重要  主要用这个

<script>

    function f(){

            $.ajax({
               url:"http://127.0.0.1:8002/SendAjax/",
               dataType:"jsonp",            //必须有,告诉server,这次访问要的是一个jsonp的结果
               jsonp: 'callbacks',          //jQuery帮助随机生成的:callbacks="wner"
           //这里没有写jsonpCallback :URL就会形成 "http://......./?callbacks=231das4" 后是生成的随机字符串
success:function(data){ //返回的内容 比如返回的是 213das4('{"name":"alxe"}')
        // 执行213das4('{"name":"alex"}') 123das4实际就是指的是success这个函数的内存空间
4        //所以执行它就是执行success后边的匿名函数 alert(
"hi "+data) } }); } </script>

 jsonp: 'callbacks'就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名'SayHi',server端接受callback键对应值后就可以在其中填充数据打包返回了; 

jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,那前端就得用回调函数来处理对应数据了。利用jQuery可以很方便的实现JSONP来进行跨域访问。  

注意 JSONP一定是GET请求

 应用

跨域来实现将电视台的界面单返回出来

先查看这个连接能够得到什么内容  通过连接可以看到是得到的这些内容

 

 

 

 

 

 

 

 $('.b3').click(function () {
        $.ajax({url:'http://www.jxntv.cn/data/jmd-jxtv2.html?',
            dataType:'jsonp',
            jsonp:'callback',
jsonpCallback:'list' 这里这样写就是为了返回的函数是以 list(内容)的形式
              所以在ajax这里边也不用写success的函数了 直接写下边的的函数的内容
}) });

js中的函数

  function list(data) {  #这个名字这样写就是为了接收上边返回回来的函数并且执行
        console.log(data.data);
      #打印出来的数据如下
var a=data.data; {# #} $.each($(a),function(key,i){ {# 用each循环将内容取出来 只取了其中的week字段 #} console.log(i["week"]);
{# 将取出的week中的字段拼接成一个标签,用js将内容返回给HTML页面y用于显示#} c='
<h4>'+i["week"] +'</h4>'; $('.show').append(c); var b=i["list"];


            $.each($(b),function (key,j) {
{#取出数据,将数据拼接在模板语言中返回给页面#} d='
<span><a href='+j['link'] +'>'+ j['name']+'</a></span>'; $('.show').append(d); console.log(j['name'],j['link']) }) }) }

 实现效果

 

实现代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
<h4>这是测试跨域的</h4>
<button class="b3">点击3</button>
<hr>
<div class="show">

</div>
<script src="/static/js/jquery-3.2.1.min.js/"></script>

<script>

    $('.b3').click(function () {
        $.ajax({url:'http://www.jxntv.cn/data/jmd-jxtv2.html?',
            dataType:'jsonp',
            jsonp:'callback',
            jsonpCallback:'list'
            })                 

    });
    function list(data) {
        console.log(data.data);
        var a=data.data;
{#        #}
        $.each($(a),function(key,i){
{#            #}
            console.log(i["week"]);
            c='<h4>'+i["week"] +'</h4>';
            $('.show').append(c);
            var b=i["list"];
            $.each($(b),function (key,j) {
                d='<span><a href='+j['link'] +'>'+ j['name']+'</a></span>';
                   $('.show').append(d);
                console.log(j['name'],j['link'])
            })
        })

    }


</script>





</body>
</html>
View Code

 

第二中方案

2、CORS

随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(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
 
注意:同时满足以上两个条件时,则是简单请求否则为复杂请求

* 简单请求和非简单请求的区别?

1
2
简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。

* 关于“预检”

1
2
3
4
5
6
7
- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers

 

基于cors实现AJAX请求:

 

a.基本跨域:

被访问的页面的设置

 

def service(request):
                    v1 = request.GET.get('v1')
                    v2 = request.GET.get('v2')
                    result = v1 + v2
                    obj =  HttpResponse(result)
            #这样设置是可以允许这两个地址来跨域访问此试图返回的内容
# obj['Access-Control-Allow-Origin'] = 'http://localhost:63342,http://localhost:63343,'
            #允许所有的跨域请求访问
obj['Access-Control-Allow-Origin'] = '*' return obj

 

发送跨域请求的也页面端   通过ajax来发送

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
<h3>这是发送跨域请求的页面</h3>
<button type="submit" onclick="a()"></button>
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
    function a(){
        $.ajax({
            url:'http://127.0.0.1:8009/test/?v1=haha&v2=哼歌',
            type:'get',
            success:function (arg) {
                alert(arg)
            }
            }
        )

    }
</script>

</body>

</html>

b. 自定义请求头:先预检允许头,再发起数据相关请求。

 

  

 

 被访问的页面的设置

def test(request):
    #当自定义请求头时  request.method为OPTIONS
    if request.method=="OPTIONS":
        print('进行预检')
        v1 = request.GET.get('v1')
        v2 = request.GET.get('v2')

        result = v1 + v2
        obj = HttpResponse('nihhao a ')
        #设置允许请求头中带有k1字段
        obj["Access-Control-Allow-Headers"] = 'k1'
        obj["Access-Control-Allow-Origin"] = 'http://localhost:63342'
        return obj
    v1 = request.GET.get('v1')
    v2 = request.GET.get('v2')

    result = v1 + v2
    obj=HttpResponse('nihhao a ')
    obj["Access-Control-Allow-Origin"]='http://localhost:63342'
    return obj

 

 

发送跨域请求的也页面端   通过ajax来发送

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
<h3>这是发送跨域请求的页面</h3>
<button type="submit" onclick="a()"></button>
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
    function a(){
        $.ajax({
            url:'http://127.0.0.1:8009/test/?v1=haha&v2=哼歌',
            type:'get',
//            这是自定义的请求头,定义他之后  请求的method会发生变化
          headers:{"k1":"aaa"},
            success:function (arg) {
                alert(arg)
            }
            }
        )
    }
</script>
</body>

</html>

 

c. 不寻常的方法:先预检允许方法,再发起数据相关请求。 

  被访问的页面的设置

def test(request):
    print(request.method)
    #当请求的type="PUT"的时候 request.method为OPTIONS
    if request.method=="OPTIONS":
        print('进行预检')
        print(request.method)
        v1 = request.GET.get('v1')
        v2 = request.GET.get('v2')
        result = v1 + v2
        obj = HttpResponse('nihhaSSSSo a ')
        #设置允许请求方法为PUT的请求
        obj["Access-Control-Allow-Methods"] = 'PUT'
        obj["Access-Control-Allow-Origin"] = 'http://localhost:63342'
        return obj
    v1 = request.GET.get('v1')
    v2 = request.GET.get('v2')

    result = v1 + v2
    obj=HttpResponse('nihhao a ')
    obj["Access-Control-Allow-Origin"]='http://localhost:63342'
    return obj

发送跨域请求的也页面端   通过ajax来发送

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
<h3>这是发送跨域请求的页面</h3>
<button type="submit" onclick="a()"></button>
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
    function a(){
        $.ajax({
            url:'http://127.0.0.1:8009/test/?v1=haha&v2=哼歌',
//            自定义请求方式
            type:'PUT',
            success:function (arg) {
                alert(arg)
            }
            }
        )
    }
</script>
</body>

</html>

 

b、支持跨域,复杂请求

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

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

 

c、跨域获取响应头

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

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                    // 获取响应头
                    console.log(xhr.getAllResponseHeaders());
                }
            };
            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                    // 获取响应头
                    console.log(xmlHttpRequest.getAllResponseHeaders());
                }
            })
        }


    </script>
</body>
</html>
class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")

        self.set_header('xxoo', "seven")
        self.set_header('bili', "daobidao")

        self.set_header('Access-Control-Expose-Headers', "xxoo,bili")


        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)
View Code

d、跨域传输cookie

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

如果想要发送:

  • 浏览器端:XMLHttpRequest的withCredentials为true
  • 服务器端:Access-Control-Allow-Credentials为true
  • 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="submit" onclick="XmlSendRequest();" />
    </p>

    <p>
        <input type="submit" onclick="JqSendRequest();" />
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function XmlSendRequest(){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4) {
                    var result = xhr.responseText;
                    console.log(result);
                }
            };

            xhr.withCredentials = true;

            xhr.open('PUT', "http://c2.com:8000/test/", true);
            xhr.setRequestHeader('k1', 'v1');
            xhr.send();
        }

        function JqSendRequest(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'PUT',
                dataType: 'text',
                headers: {'k1': 'v1'},
                xhrFields:{withCredentials: true},
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>
View Code

 

class MainHandler(tornado.web.RequestHandler):
    
    def put(self):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Credentials', "true")
        
        self.set_header('xxoo', "seven")
        self.set_header('bili', "daobidao")
        self.set_header('Access-Control-Expose-Headers', "xxoo,bili")

        self.set_cookie('kkkkk', 'vvvvv');

        self.write('{"status": true, "data": "seven"}')

    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
        self.set_header('Access-Control-Allow-Headers', "k1,k2")
        self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
        self.set_header('Access-Control-Max-Age', 10)
View Code

 

posted on 2018-03-01 20:33  王大拿  阅读(207)  评论(0)    收藏  举报

导航