【转】同源策略和跨域请求解决方案

1、一个源的定义

若是两个页面的协议,端口(若是有指定)和域名都相同,则两个页面具备相同的源。
举个例子:javascript

      下表给出了相对http://a.xyz.com/dir/page.html同源检测的示例: 
      URL                                         结果          缘由
      http://a.xyz.com/dir2/other.html            成功     协议,端口(若是有指定)和域名都相同
      http://a.xyz.com/dir/inner/another.html     成功    协议,端口(若是有指定)和域名都相同 
      https://a.xyz.com/secure.html               失败    不一样协议 ( https和http )
      http://a.xyz.com:81/dir/etc.html            失败    不一样端口 ( 81和80)
      http://a.opq.com/dir/other.html             失败    不一样域名 ( xyz和opq)

 

2、同源策略是什么?

同源策略是浏览器的一个安全功能,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。因此xyz.com下的js脚本采用ajax读取abc.com里面的文件数据是会被拒绝的。css

同源策略限制了从同一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。html

3、基于jsonp实现的跨域请求

  1. 页面中的连接,重定向以及表单提交是不会受到同源策略限制的。java

  2. 跨域资源的引入是能够的。可是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。jquery

下面来分步举例详细阐述其中的奥妙:ajax

一、先开两个项目,

项目1(http://127.0.0.1:8000/)
项目2(http://127.0.0.1:8100/)django

项目1json

url:
url(r'index1/$',views.index1)

views:

def index1(request):
return  HttpResponse('wangjifei')

 

项目2后端

url:
url(r'index2/$',views.index2)

views  :
def index2(request):
    return render(request,'index2.html')

index2.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>同源策略</title>
</head>
<body>

<button id="btn">提交</button>

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script>
    $('#btn').click(function () {
        $.ajax({
            url:"http://127.0.0.1:8000/index1/",
            type:'get',
            success:function (res) {
                console.log(res)
           }
        })
    })
</script>
</body>
</html>

 

如今,打开使用浏览器打开 http://127.0.0.1:8100/index2/,点击页面上的 '提交' 按钮,会在console页面发现错误信息以下:跨域

 

 

为何报错呢?由于同源策略限制跨域发送ajax请求。
细心点的同窗应该会发现咱们的demo1项目其实已经接收到了请求并返回了响应,是浏览器对非同源请求返回的结果作了拦截。
再细心点的同窗会发现,咱们使用cdn方式引用的jQuery文件也是跨域的,它就可使用。
一样是从其余的站点拿东西,script标签就能够。那咱们能不能利用这一点搞点事情呢?

二、把index2.html中的代码改一下:

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

<button id="btn">提交</button>

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script src="http://127.0.0.1:8000/index1/"></script>
</body>
</html>

 

如今刷新一下会出现以下错误:

 

 
 

看来后端返回的响应已经被拿到了,只不过把wangjifei当成了一个变量来使用,可是该页面上却没有定义一个名为wangjifei的变量。因此出错了。

三、那咱们就在index2.html中定义一个wangjifei变量看看:

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

<button id="btn">提交</button>

<script>
    var wangjifei = 123
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script src="http://127.0.0.1:8000/index1/"></script>
</body>
</html>

 

刷新发现不报错了,

 

 

四、我定义一个变量能够,那可不能够定义一个函数呢?

index2.html

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

<button id="btn">提交</button>

<script>
    function wangjifei() {
        console.log('出手就要专业')
    }
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script src="http://127.0.0.1:8000/index1/"></script>
</body>
</html>

 

项目1中的views:也修改一下

def index1(request):
return  HttpResponse('wangjifei()')

 

刷新一下页面显示结果:

 

 

 

结果分析:返回的 wangjifei(),页面上拿到这个响应以后直接执行了wangjifei函数!

五、那函数中可不能够传递参数呢?咱们试一下!

index2.html

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

<button id="btn">提交</button>

<script>
    function wangjifei(res) {
         console.log(res)
    }
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script src="http://127.0.0.1:8000/index1/"></script>
</body>
</html>

 

项目1中的 views

from django.http import HttpResponse
import json

def index1(request):
    ret={'code':1,'msg':[110,119,120,12306]}
    res = json.dumps(ret)
    return  HttpResponse(f'wangjifei({res})')

 

刷新以后显示结果:

 

 

果真传递参数也是能够的!咱们经过script标签的跨域特性来绕过同源策略拿到想要的数据了!!!

这其实就是JSONP的简单实现模式,或者说是JSONP的原型:建立一个回调函数,而后在远程服务上调用这个函数而且将JSON 数据形式做为参数传递,完成回调。
将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。
可是咱们更多时候是但愿经过事件触发数据的获取,而不是像上面同样页面一刷新就执行了,这样很不灵活。

六、咱们能够经过javascript动态的建立script标签来实现。

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

<button id="btn">提交</button>

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script>
    //自定义的函数
    function wangjifei(res) {
        console.log(res)
    }
    //jquery给button绑定点击事件
    $('#btn').click(function () {
        //建立一个script标签
        var scriptEle = document.createElement('script');
        //给标签添加src属性,并添加对应的属性值    http://127.0.0.1:8000/index1
        $(scriptEle).attr('src','http://127.0.0.1:8000/index1');
        //将建立好的标签添加到页面中,标签添加后就会自动触发get请求
        $('body').append(scriptEle);
        //将标签移除
        $(scriptEle).remove()
    })
</script>
</body>
</html>

 

这样当咱们点击button按钮的时候,会在页面上插入一个script标签,而后从后端获取数据后再删除掉。

七、为了实现更加灵活的调用,咱们能够把客户端定义的回调函数的函数名传给服务端,服务端则会返回以该回调函数名,将获取的json数据传入这个函数完成回调。这样就能实现动态的调用了。修改代码以下:

index2.html代码:

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

<button id="btn">提交</button>

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script>
    //自定义的函数
    function xxx(res) {
        console.log(res)
    }
    //jquery给button绑定点击事件
    $('#btn').click(function () {
        //建立一个script标签
        var scriptEle = document.createElement('script');
        //给标签添加src属性,并添加对应的属性值    http://127.0.0.1:8000/index1?callback=xxx
        $(scriptEle).attr('src','http://127.0.0.1:8000/index1?callback=xxx');
        //将建立好的标签添加到页面中,标签添加后就会自动触发get请求
        $('body').append(scriptEle);
        //将标签移除
        $(scriptEle).remove()
    })
</script>
</body>
</html>

 

项目1中views:

from django.http import HttpResponse
import json

def index1(request):
    ret={'code':1,'msg':[110,119,120,12306]}
    res = json.dumps(ret)
    callback = request.GET.get('callback')
    return  HttpResponse(f'{callback}({res})')

 

4、jQuery中getJSON方法介绍:

一、jQuery中有专门的方法实现jsonp。

index2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>同源策略</title>
</head>
<body>
<button id="btn">提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">        </script>
<script>
    //jquery给button绑定点击事件
    $('#btn').click(function () {
        $.getJSON("http://127.0.0.1:8000/index1?callback=?",function (res) {
            console.log(res)
        })
    })
</script>
</body>
</html>

 

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

二、可是若是咱们想本身指定回调函数名,或者说服务上规定了回调函数名该怎么办呢?咱们可使用$.ajax方法来实现:

index2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>同源策略</title>
</head>
<body>
<button id="btn">提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script>
    //jquery给button绑定点击事件
    $('#btn').click(function () {
        $.ajax({
            //要访问的url
            url:"http://127.0.0.1:8000/index1/",
            //要处理的数据类型jsonp
            dataType:'jsonp',
            //自定义回调函数名必要参数
            jsonp:'callback',
            //自定义回调函数名,url中callback=后面的函数名
            jsonpcallback:'wangjifei'
        })
    });
    //回调函数
    function wangjifei(res) {
        console.log(res)
    }
</script>
</body>
</html>

 

views:

from django.http import HttpResponse
import json

def index1(request):
    ret={'code':1,'msg':[110,119,120,12306]}
    res = json.dumps(ret)
    callback = request.GET.get('callback')
    return  HttpResponse(f'wangjifei({res})')

 

三、用ajax技术一般将回调函数写在成功回调函数的位置:

index2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>同源策略</title>
</head>
<body>
<button id="btn">提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">    </script>
<script>
    //jquery给button绑定点击事件
    $('#btn').click(function () {
        $.ajax({
            //要访问的url
            url:"http://127.0.0.1:8000/index1/",
            //要处理的数据类型jsonp
            dataType:'jsonp',
            //success回调
            success:function (res) {
                console.log(res)
            }
        })
    });
    //回调函数
    function wangjifei(res) {
        console.log(res)
    }
</script>
</body>
</html>

 

最后来一个jsonp的实际应用:

 <!DOCTYPE html>
     <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>同源策略</title>
    </head>
    <body>
    <button id="show-tv">提交</button>
    <div class="tv-list"></div>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
        $("#show-tv").click(function () {
            $.ajax({
                url: "http://www.jxntv.cn/data/jmd-jxtv2.html?  callback=list&_=1454376870403",
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallback: 'list',
                success: function (data) {
                    var weekList = data.data;
                    console.log(weekList);
                    var $tvListEle = $(".tv-list");
                    $.each(weekList, function (k, v) {
                        var s1 = "<p>" + v.week + "列表</p>";
                        $tvListEle.append(s1);
                        $.each(v.list, function (k2, v2) {
                            var s2 = "<p><a href='" + v2.link + "'>" + v2.name + "</a></p>";
                            $tvListEle.append(s2)
                        });
                        $tvListEle.append("<hr>");
                    })
                }
            })
        });
    </script>
    </body>
    </html>

 

5、基于Core方法解决跨域请求

  • 咱们介绍了jsonp解决跨域请求问题,这种解决方式很好的诠释了跨域请求的本质,可是略显麻烦,是否还记得在咱们不作任何处理的时候,跨域请求时候浏览器给咱们报的错误不?翻译过来就是由于响应头没有指定Access-Control-Allow-Origin所容许原始的请求路径,所以原始请求路径http://127.0.0.1:8001不被容许访问。 基于上述的缘由解释,咱们只须要在响应的内容加入上述这样的受权字段,即可解决。

  • 简单请求的定义:
    只要同时知足如下两大条件,就属于简单请求,不知足就是复杂请求!!!
    1.(1) 请求方法是如下三种方法之一:
    -- HEAD,GET,POST
    2.(2)HTTP的头信息不超出如下几种字段:
    -- Accept
    -- Accept-Language
    -- Content-Language
    -- Last-Event-ID
    -- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

因为django的全部请求响应都要走中间件,因此能够写一个跨域的中间件来解决跨域问题

from django.utils.deprecation import MiddlewareMixin
class MyCore(MiddlewareMixin):
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = "*"  //简单请求
        if request.method == "OPTIONS":
            # 复杂请求 预检
            response['Access-Control-Allow-Headers'] = "Content-Type"
            response['Access-Control-Allow-Methods'] = "POST, DELETE, PUT"
        return response

 

 
相关文章
相关标签/搜索