• 同源策略与跨域请求


    一、同源策略

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

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

    jsonp(jsonpadding)

    之前发ajax的时候都是在自己给自己的当前的项目下发

    现在我们来实现跨域发。给别人的项目发数据,

    创建两个项目,先来测试一下

    项目一:Http://127.0.0.1:8006

    views.py

    from django.shortcuts import render,HttpResponse
    
    def index(request):
        return render(request,"index.html")
    
    def service(request):
        return HttpResponse("Project 1")

    index.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    
    <h3>INDEX</h3>
    
    <button class="get_service">Project 1</button>
    
    <script src="/static/jquery-3.3.1.js"></script>
    <script>
        $(".get_service").click(function () {
            alert(123);
            $.ajax({
                url: "http://127.0.0.1:8008/service/", 
                success: function (data) {
                    console.log(data)
                }
            })
        });
    
    </script>
    
    </body>
    </html>

    项目二:Http://127.0.0.1:8006

    views.py

    from django.shortcuts import render,HttpResponse
    
    def index(request):
    
        return render(request,"index.html")
    
    def service(request):
    
        return HttpResponse("Project 2")

    访问项目一的index界面

     当点击Project 1 按钮时本应该跳转到项目二的service路径下给我们回一个响应,但此时报了一个错误:

     这是因为同源策略给限制了,这是游览器给我们报的一个错。我们可以看到弹出了alert框,说明去项目二拿到了数据,只是浏览器不给我们显示。

    注意:a标签,form,img标签,引用cdn的css等也属于跨域(跨不同的域拿过来文件来使用),不是所有的请求都给做跨域,(为什么要进行跨域呢?因为我想用人家的数据,所以得去别人的url中去拿,借助script标签)

    只有发ajax的时候给拦截了,所以要解决的问题只是针对ajax请求能够实现跨域请求

    二、解决同源策略的两种方法

    1、基于jsonp实现跨域请求

    引用:

    什么是jsonp: 

    为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
    jsonp原理:

    ajax请求受同源策略影响,不允许进行跨域请求,而script标签src属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。

    跨域的原理
           1:使用script 标签发送请求,这个标签支持跨域访问
           2:在script 标签里面给服务器端传递一个 callback
           3:callback 的值对应到页面一定要定义一个全局函数(为什么是全局?因为服务端接收到callback函数后会返回页面中的script中去找,如果不写在全局作用域中根本找不到)
           4:服务端返回的是一个函数的调用。调用的时候会吧数据作为参数包在这个函数里面。

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

    jsonp是用来解决跨域请求的问题,原理是通过script标签的跨域特性来绕过同源策略。

    项目一:

    index.html

    <script>   
        $(
    ".get_service").click(function () { $.ajax({ url: "http://127.0.0.1:8008/service/", type: "get", dataType: "jsonp", // 必须有,告诉service这次访问的是一个jsonp的结果 jsonp: 'callbacks', //jsonpCallback:"alex", success: function (data) { //ajax拿到返回值(随机字符串)之后立即执行函数 console.log(data) } }) }); </script>

    jsonp指定使用哪个名字将回调函数传给服务端,也就是在服务端通过 request.getParameter(""); 的那个名字,

    jsonpCallback就是request.getParamete("")取得的值,也就是回调函数的名称。

    这两个参数都可以不指定,只要我们是通过 success 来指定回调函数的情况下,就可以省略这两个参数,jsnop如果不指定,默认是 "callback",jsnpCallback不指定,是jquery自动生成的一个函数名称  

    注意 JSONP一定是GET请求

    项目二:

    views.py

    from django.shortcuts import render,HttpResponse
    import json
    def service(request):  # jsonp
    
        func=request.GET.get("callbacks")
        print("func",func)    #func jQuery33105914359147116615_1589618121318
        info={"name":"egon","age":34,"price":200}
        return HttpResponse("%s ('%s')"%(func,json.dumps(info)))

     

     再次访问项目一的index界面,点击Project 1按钮在控制台上就展现了我们向项目二所拿到的数据了。

    2、基于cors实现跨域请求(比较常见的方式,简单)

    只需在响应体中对 Access-Control-Allow-Origin进行设置

    项目一:

    index.html 

    <script src="/static/jquery-3.3.1.js"></script>
    <script>    $(".get_service").click(function () {
    
            $.ajax({
                url: "http://127.0.0.1:8008/service/",
                success: function (data) {
                    console.log(data)
                }
            })
        });
    
    </script>
    

     在最开始我们就用上面的形式对项目二的service进行访问,但被浏览器拦截了,接下来我们只需在响应体中对 Access-Control-Allow-Origin进行设置就能够

    实现跨域请求。

    项目二:

    view.py

    def service(request):
    
        info={"name":"egon","age":34,"price":200}
    
        response=HttpResponse(json.dumps(info))
        response["Access-Control-Allow-Origin"]="http://127.0.0.1:8006" #只要是这个域来访问我就通过
        #response["Access-Control-Allow-Origin"]="*"  #所有域都能进行访问
        return  response

    而且我们发现这种方式实现的跨域比基于jsonp实现跨域请求较为简单!

     JSONP和CORS的区别 

      JSONP:服务端不用修改,需要改前端。发jsonp请求 

      JSONP:只能发GET请求 

      CORS:前端的代码不用修改,服务端的代码需要修改。如果是简单请求的话在服务端加上一个响应头。 可以发任意请求 

    三、应用

    // 跨域请求实例
        $(".get_service").click(function () {
    
            $.ajax({
                url:"http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403",
                 dataType: 'jsonp',
                 jsonp: 'callback',
                 jsonpCallback: 'list',
                 success:function (data) {
                     console.log(data.data);   //  [{},{},{},{},{},{}]
                     week_list=data.data;
                     
                     $.each(week_list,function (i,j) {
                         console.log(i,j);  // 1 {week: "周一", list: Array(19)}
                         s="<p>"+j.week+"列表</p>";
                         $(".show_list").append(s);
    
                         $.each(j.list,function (k,v) {  // {time: "0030", name: "通宵剧场六集连播", link: "http://www.jxntv.cn/live/jxtv2.shtml"}
                              a="<p><a href='"+v.link+"'>"+v.name+"</a></p>";
                              $(".show_list").append(a);
                         })
                     })
                     
                 }
            })
    
        })

     

    附:

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    
    <h3>INDEX</h3>
    
    <button class="get_service">Project 1</button>
    
    
    <script src="/static/jquery-3.3.1.js"></script>
    <script>
    
        $(".get_service-0").click(function () {
            alert(123);
            $.ajax({
    
                url: "http://127.0.0.1:8008/service/",
                success: function (data) {
                    console.log(data)
                }
            })
        });
    
        function alex(arg) {
            console.log(arg);
            console.log(typeof arg);
            var data = JSON.parse(arg);
            console.log(data);
            console.log(typeof data);
        }
    
        function get_jsonp_data(url) {
            var ele_script = $("<script>");
            <!--创建scr标签-->
            ele_script.attr("src", url);
            <!--为标签添加src属性-->
            ele_script.attr("id", "jsonp");
            <!--为标签添加id属性-->
            $("body").append(ele_script);
            <!--把标签添加到body中-->
            $("#jsonp").remove()
        }
    
        <!--标签添加到body中后就已经执行了,执行之后删除标签-->
    
        $(".get_service").click(function () {
    
            $.ajax({
                url: "http://127.0.0.1:8008/service/",
                success: function (data) {
                    console.log(data)
                }
            })
        });
    
    
        $(".get_service-2").click(function () {
    
            get_jsonp_data("http://127.0.0.1:8008/service/?callbacks=alex")
    
        });
    
        // 终极形式
    
        $(".get_service-3").click(function () {
    
            $.ajax({
    
                url: "http://127.0.0.1:8008/service/",
                type: "get",
                dataType: "jsonp",     // 伪造ajax  基于script
                jsonp: 'callbacks',
                //jsonpCallback:"alex",
                success: function (data) {  //ajax拿到返回值(随机字符串)之后立即执行函数
                    console.log(data)
                }
            })
        });
    
        // 应用
    
        $(".get_service-4").click(function () {
    
            $.ajax({
                url: "http://www.jxntv.cn/data/jmd-jxtv2.html",
                type: "get",
                dataType: "jsonp",     // 伪造ajax  基于script
                jsonp: 'callbacks',
                jsonpCallback: "list",
                success: function (data) {
                    //console.log(data.data);
    
                    var html = "";
                    $.each(data.data, function (index, weekday) {
                        console.log(weekday); // {week: "周一", list: Array(19)}
    
                        html += '<p>' + weekday.week + '</p>';
    
                        $.each(weekday.list, function (j, show) {
                            html += '<p><a href=' + show.link + '>' + show.name + '</a></p>'
                        })
                    });
                    $("body").append(html)
                }
            })
        })
    </script>
    
    </body>
    </html>
    实现跨域请求的流程

    参考1

    参考2

  • 相关阅读:
    从Github上将laravel项目拉到新开发环境
    Nginx-Primary script unknown的报错的解决方法
    CentOS 7 安装PHP7+Nginx+Mysql5.7开发环境
    程序员面试经常会被问到的12个问题
    IOC(控制反转)的理解
    用冒泡排序的方法将数组从小到大排列
    常用设计模式详解
    PHP常见面试题总结
    能够遍历一个文件夹下的所有文件和子文件夹的函数
    线特征---LineMatching原理(四)
  • 原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/12901538.html
Copyright © 2020-2023  润新知