跨域问题
由于浏览器同源策略的限制,非同源下的请求,都会产生跨域问题,jsonp即是为了解决这个问题出现的一种简便解决方案。
可参考:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html,如何规避同源限制
1.1 含义
1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。
- 协议相同
- 域名相同
- 端口相同
举例来说,http://www.example.com/dir/page.html
这个网址,协议是http://
,域名是www.example.com
,端口是80
(默认端口可以省略)。它的同源情况如下。
http://www.example.com/dir2/other.html
:同源http://example.com/dir/other.html
:不同源(域名不同)http://v2.www.example.com/dir/other.html
:不同源(域名不同)http://www.example.com:81/dir/other.html
:不同源(端口不同)
1.2 目的
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
1.3 限制范围
随着互联网的发展,"同源政策"越来越严格。目前,如果非同源,共有三种行为受到限制。
(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(3) AJAX 请求不能发送。
虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。下面,我将详细介绍,如何规避上面三种限制。
同源策略即:同一协议,同一域名,同一端口号。当其中一个不满足时,我们的请求即会发生跨域问题。
众所周知,由于浏览器的同源策略,要从不同的域(网站)访问数据会产生跨域问题,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) 是一种机制,它使用额外的 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...
1
2
3
4
5
6
上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
withCredentials 属性
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,开发者必须在AJAX请求中打开withCredentials属性。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
1
2
否则,即使服务器同意发送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的网站请求数据。
参考:https://blog.csdn.net/qq_42376204/article/details/108568977