什么是跨域?如何解决跨域?

参考https://www.cnblogs.com/willing-shang/p/6719875.html

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

https://segmentfault.com/a/1190000011145364

背景:

由于浏览器同源策略的限制,非同源下的请求,都会产生跨域问题,jsonp即是为了解决这个问题出现的一种简便解决方案。

同源策略即:同一协议,同一域名,同一端口号。当其中一个不满足时,我们的请求即会发生跨域问题。

举个简单的例子:

  1. http://www.abc.com:3000到https://www.abc.com:3000的请求会出现跨域(域名、端口相同但协议不同)
  2. http://www.abc.com:3000到http://www.abc.com:3001的请求会出现跨域(域名、协议相同但端口不同)
  3. http://www.abc.com:3000到http://www.def.com:3000的请求会出现跨域(域名不同)

这里涉及到一个问题:什么时候端口可以省略?

假设我们需要从’www.localhost.com’发起一个获取数据的请求’www.somewhere.com/getdata’,如果有我们以ajax来发起请求,那么由于浏览器同源保护策略的限制,该请求的返回值不会被浏览器所接受,这就是跨域问题。但是script这种标签会发起一个get请求,并且这个请求是不受同源策略限制的,如果有我们将’www.somewhere.com/getdata’以script标签来发送变成如下请求方式,那么是不是就不会有跨域问题了

JSONP跨域原理(只能请求GET请求)

众所周知,由于浏览器的同源策略,要从不同的域(网站)访问数据会产生跨域问题,img的src(获取图片),link的herf(获取css),script的scr(获取JavaScript),这三个不属于同源策略,都可以跨域获取数据,因此,jsonp应运而生!

JSONP实现跨域的原理简单的说,就是动态创建script标签,然后利用script的src 不受同源策略约束来跨域获取数据。

出现了两个问题,第一,怎么使用script来发送请求,第二,请求得到的数据应该怎么在前端页面上接收并处理。对于第一个问题,我们一般会将script标签写在html文档中,当我们通常遇到的都会是动态请求,如果有我们还像原来一样把标签提前写好在html中,那么浏览器解析文档到这个script标签时就会立即发起请求,等我们想要用到这些数据时,再去找前面加载好的数据,这样显然太费时费力,不太灵活,而且页面上如果有很多请求,岂不是要提前些很多script标签在页面上,这样页面丑陋的根本没法看了。我们需要的是在请求服务的时候,再发起请求,那么我们完全可以用动态标签来实现,通多document.createElement来动态创建一个script标签,然后为其设置src属性,等请求完毕之后再将script标签移除,那么第一个问题便迎刃而解了。

let script = document.createElement('script');
srcipt.src = 'www.somewhere.com/getdata';
document.querySelector('head').appendChild(script);
<body>
    <button id="normal">原生jsonp</button>
    <script>
        function show(x) {
            console.log('下面是jsonp返回的数据')
            console.log(x)
        }
        var btn = document.getElementById('normal')
        btn.onclick = function () {
            var parent = document.body
            var cb = document.createElement('script');
            cb.src = "http://localhost:3000?callback=show"
            parent.appendChild(cb)
            parent.removeChild(parent.lastElementChild)
        }
    </script>
</body>
jsonp的缺点
  • 只能发送get请求。因为script只能发送get请求
  • 需要后台配合。此种请求方式应该前后端配合,将返回结果包装成callback(result)的形式。

CORS实现跨域

摘自MDN:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

功能概述

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

简单的概述就是,cors会携带特殊的首部origin,声明允许服务器对该浏览器有权限访问资源,CORS除了简单请求外(GET,POST)一般会有预检请求试求服务器是否允许该跨域请求,如果不行可以通过onerror捕获到,CORS请求失败会产生错误,但是为了安全,在JavaScript代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

withCredentials 属性

CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,开发者必须在AJAX请求中打开withCredentials属性。

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

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

安全隐患

如果程序猿偷懒将Access-Control-Allow-Origin设置为允许来自所有域的跨域请求。那么cors的安全机制几乎就无效了。不过先别高兴的太早。其实这里在设计的时候有一个很好的限制。xmlhttprequest发送的请求需要使用“withCredentials”来带上cookie,如果一个目标域设置成了允许任意域的跨域请求,这个请求又带着cookie的话,这个请求是不合法的。(就是如果需要实现带cookie的跨域请求,需要明确的配置允许来源的域,使用任意域的配置是不合法的)浏览器会屏蔽掉返回的结果。javascript就没法获取返回的数据了。这是cors模型最后一道防线。假如没有这个限制的话,那么javascript就可以获取返回数据中的csrf token,以及各种敏感数据。这个限制极大的降低了cors的风险。

CORS与JSONP的比较

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值