跨域的产生原因及解决方案

1、跨域的原因

跨域是是因为浏览器的同源策略限制,是浏览器的一种安全机制,服务端之间是不存在跨域的。

所谓同源指的是两个页面具有相同的协议、主机和端口,三者有任一不相同即会产生跨域。

2、跨域举例

3、跨域的解决办法

3.1 浏览器限制

既然跨域是浏览器的一种安全限制,那是否可以让浏览器不做跨域限制呢?答案是肯定的,我们设置浏览器禁止检查--disabled-web-security --user-data-dir,重新打开浏览器,会发现在浏览器顶部提示:

                                  

设置成功,跨域请求可被正常发送出去。

3.2、jsonp的实现方式

jsonp的实现原理为:虽然浏览器限制了页面直接向非同源的服务器发送ajax请求,但是并没有限制

页面向非同源的服务器请求JS脚本,就像我们可以使用标签请求任意域上图片一样。

当使用jsonp方式来实现跨域请求时,需要服务端进行配合。因为一般使用ajax请求从服务端返回的数据都是json对象,但是我们需要的是JS脚本,这就需要服务端将返回的数据形式变成JS脚本形式,所以jsonp是一种约定(实现方式将在下面进行讲解),是前后端为了实现跨域达成的一种协议。

当我们在本地请求慕课网上的地址时,控制台报错,提示跨域。(之所以请求慕课网的url,是因为慕课网支持jsonp,我们暂且理解为慕课网后台已经配合进行了改动)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script src="jquery-3.5.1.min.js"></script>
</head>
<body>
<a href="#" onclick="get1()">发生跨域请求</a>
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
<script>
    function get1() {
        $.ajax({
            url:"https://www.imooc.com/index/getstarlist",
            type: "GET"
        })
    }
</script>
</body>
</html>

修改请求的方式,使用jsonp的方式发送跨域请求。在ajax请求中设置dataType="jsonp",

        $.ajax({
            url:"https://www.imooc.com/index/getstarlist",
            type: "GET",
            dataType:"jsonp"
        })

这时候看到请求成功了。

 

服务端返回结果为:

 

细心观察发现,getstarlist请求后面自动跟上了callback参数,参数的值与服务端返回的函数名称相同

由此可以得出jsonp的实现原理为:当前端请求设置dataType="jsonp"时,浏览器会自动在url中添加callback="jqueryxxx"请求参数。对应服务器在接收到callback参数时,知道了这是一个jsonp请求,就会自动把返回的json对象转化为"jqueryxxx(json对象)"脚本的形式返回。

那为什么将dataType="jsonp"的形式,跨域请求就能成功呢?原因是因为当设置ajax请求的dataType="jsonp"时,实际上是在<html>的<head>中动态添加了一条<script>标签。

通过在开发者工具下的source下的jquery包中打断点,

刷新页面,可以看到dom元素中多了一条<script>标签,这条标签便是浏览器向服务端请求的JS脚本。

所以,jsonp向服务端发送的是script脚本,而普通的ajax请求发送的是xhr请求,这也是为什么使用jsonp能实现跨域的原因。

请求回来的js脚本(函数名称包裹的json对象)自动执行,默认success()函数作为回调函数。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script src="jquery-3.5.1.min.js"></script>
</head>
<body>
<a href="#" onclick="get1()">发生get1请求</a>
<textarea id="text" style="width: 400px; height: 100px;"></textarea>
<script>
    function get1() {
        $.ajax({
            url:"https://www.imooc.com/index/getstarlist",
            type: "GET",
            dataType:"jsonp",
            success:function(data){
             var result = JSON.stringify(data); //json对象转成字符串
                 $("#text").val(result);
            }
        })
    }
</script>
</body>
</html>

页面展示效果:

          

Jsonp的弊端:

虽然jsonp解决了跨域问题,但是存在以下几点弊端:

(1)jsonp需要服务端配合修改,如果调用的接口不是我们自己的服务器上,jsonp就无能无力了.

(2)jsonp只能发送get请求

(3)jsonp发送的时script请求,并不是xhr请求,因此xhr的很多优势都不能使用(例如:异步,事件等)

3.3 跨域资源共享cors解决跨域

在了解cors解决跨域之前,我们先来了解一下什么是简单请求和非简单请求。

工作中比较常见的简单请求:

  1. 请求方法为:HEAD、GET、POST中的一种。
  2. HTTP请求头中字段不超过:Accept、Accept-Language、Content-Language、Last-Event-ID
  3. Content-Type字段值为application/x-www-form-urlencodedmultipart/form-datatext/plain中的一种。

满足以上条件的即为简单请求,否则即为非简单请求。

工作中常见的非简单请求:

  1. 请求方法为put、delete.
  2. 发送JSON格式的ajax请求。
  3. http中带自定义请求头。

对于简单请求:

浏览器发现是跨域请求,就会自动在请求头中加上Origin字段,代表请求来自哪个域(协议+主机名+端口号)。服务器在收到请求后,根据请求头中Origin字段值来判断是否允许跨域请求通过。具体实现方法是:在响应头Access-Control-Allow-Origin字段中设置指定的域名,表示允许这些域名的跨域请求。如果请求头中Origin字段的域名包含在这些域名中,则可以实现跨域请求(当然有时候还需要结合其他字段来判断),否则不通过。例如:

请求头信息:

GET /cors HTTP/1.1
Origin: http://localhost:8080/
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Origin字段说明本次请求来自:http://localhost:8080/

响应头信息:

Access-Control-Allow-Origin: http://localhost:8080/
Access-Control-Allow-Credentials: true
Content-Type: text/html; charset=utf-8

说明 http://localhost:8080/在服务器允许的范围内。

Access-Control-Allow-Credentials字段代表服务器允许cookie可以包含在请求中,一起发送给服务器,值为布尔类型。如果要把cookie一起发送到服务器,还需要在请求中打开withCredentials属性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

注意:如果要发送cookie,Access-Control-Allow-Origin的值不能为“*”,只能是具体的域名。

非简单请求:

非简单请求在发送http请求时,会预先发送一次“预检”(OPTIONS)请求。预检请求会事先询问服务器,当前域名是否在服务器允许的范围内,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复后,浏览器才会真正发出http请求,否则就会报错。例如:

var url = 'http://localhost:8080/';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('Header-1', 'value');
xhr.send();

上述代码中,http发送了一条PUT请求,并且自定义请求头信息Header-1.

Access-Control-Allow-Origin: http://localhost:8080/
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Header-1
Content-Type: text/html; charset=utf-8
Access-Control-Max-Age: 3600

上述http响应中,说明服务器允许http://localhost:8080/请求数据,

Access-Control-Max-Age表示本次预检请求的有限期,单位为秒,在此期间内,不用发出另一条预检请求。

一旦服务器通过了“预检”请求,以后每次浏览器正常请求CORS请求,就跟简单请求一样。会有Origin字段,响应头里也会有对应的Access-Control-Allow-Origin字段。

CORS相比较JSONP的优势:

CORS支持所有类型的http请求,jsonp只支持get请求。JSONP的优势在于支持老式浏览器,以及向不支持CORS的网站请求数据。

3.4、nginx/apache/webpack中做相应配置

 

参考资料:

http://www.ruanyifeng.com/blog/2016/04/cors.html

https://www.cnblogs.com/chiangchou/p/jsonp.html

https://www.imooc.com/learn/947

 

  • 6
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值