前言
其实在我们用ajax传输数据时,很多时候都是在做跨域请求,如果没租过服务器的小伙伴可能没去捣鼓过这东西是什么。跨域其实在我们前后端数据交互非常常见,但是现在的模块化时代,一般我们不怎么会接触到跨域问题,因为很多事API已经帮我们解决了。接下来我就给大家好好捋清跨域是怎么回事。
一、同源策略
在先介绍跨域之前我觉得必须先普及同源策略的概念。
什么是同源?
如果两个页面的协议,域名和端口都相同,则两个页面具有相同的源。
例如,下表给出了相对于http://www.test com/index.html页面的同源检测:
为了防止有小萌新连协议域名这种东西都不懂,在这里简单说明一下。
协议就是我们网址上的http协议或者https协议,域名则是网址的名字,其实每台服务器的初始名字并不是这样的有辨识度,最开始大家的服务器都是像类似这种127.0.0.1编号的名字,后面为了让网站容易辨认,才弄出了像www.baidu.com,有辨识度的名字。端口就很容易了,就是服务器的接口。
回到正题
什么是同源策略?
同源策略(英文全称Same origin policy)是浏览器提供的一个安全功能。
MDN官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。 这是一个用于隔离潜在恶意文件的重要安全机制。
说白了就是浏览器规定,A网站的JavaScript,不允许和非同源的网站C之间,进行资源的交互,例如:
①无法读取非同源网页的Cookie、LocalStorage 和IndexedDB
②无法接触非同源网页的DOM
③无法向非同源地址发送Ajax请求
二、跨域
1.什么是跨域?
同源指的是两个URL的协议、域名、端口一致,反之,则是跨域。
出现跨域的根本原因:浏览器的同源策略不允许非同源的URL之间进行资源的交互。
比如下面两个域名不同,则他们之间的数据交互就属于跨域
网页: http://www.test.com/index.html
接口: http://www.api.com/userlist
2.浏览器是如何对跨域进行拦截的?
一图胜千言
说白了就是浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到。
3.如何实现跨域数据请求?
解决跨域请求的方法有很多,我先来介绍一下两种最主要的解决方案,分别是JSONP和CORS.
JSONP:出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持GET请求,不支持POST请求。
CORS:出现的较晚,它是W3C标准,属于跨域Ajax请求的根本解决方案。支持GET和POST请求。缺点是不兼容某些低版本的浏览器。
三、JSONP
1.JSONP是什么?
JSONP (JSON with Padding)是JSON的一种"使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
2.JSONP的实现原理
由于浏览器同源策略的限制,网页中无法通过Ajax请求非同源的接口数据。但是由于<script>
标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本。
说白了,JSONP的实现原理,就是通过<script>
标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。
所以JSONP的原理其实有一点套娃的意思,来使用回调函数去"骗过"浏览器。
3.Jquery提供的JSONP接口
jQuery提供的ajax()函数,除了可以发起真正的Ajax数据请求之外,还能够发起JSONP数据请求。用法和ajax差不多。
例如:
$.ajax({
url :'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age= 20',
//如果要使用$.ajax() 发起JSONP请求,必须指定datatype 为jsonp
dataType:'jsonp',
success: function(res) {
console. log (res)
})
默认情况下,使用JQuery发起JSONP请求,会自动携带一个callback=jQueryxx的参数,JQueryxxx 是随机生成的一个回调函数名称。
jQuery中的JSONP,也是通过<script>
标签的src属性实现跨域数据访问的,只不过, JQuery 采用的是动态创建和移除<script>
标签的方式,来拨起JSONP数据请求。
●在发起JSONP请求的时候,动态向<header>
中append一个<script>
标签;
●在JSONP请求成功以后,动态从<header>
中移除刚才append进去的<script>
标签;
四、CORS
CORS其实就是跨域资源共享,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
前端JQuery.ajax设置,根据xhr.withCredentials字段判断是否带有cookie
$.ajax({
url: 'http://ajax.frontend.itheima.net:3006',
type: 'get',
data: {},
xhrFields: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
});
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信 没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附 加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
因此很多时候我们在用ajax发跨域请求而不会意识到的原因,就是后端服务器已经用CORS接口做好跨源通信了。
- CORS响应头部- Access-Control-Allow-Origin
响应头部中可以携带一个 Access-Control-Allow-Origin段,其语法如下:
Access-Control- Allow-Origin: <origin>| *
其中,origin 参数的值指定了允许访问该资源的外域URL。
//例如,下面的字段值将只允许来自http://itcast.cn 的请求:
res.setHeader( 'Access-Control-Allow-Origin', 'http://itcast.cn )
//允许全部的请求:
res.setHeader( 'Access-Control-Allow-Origin', '*' )
- CORS响应头部- Access-Control-Allow-Headers
默认情况下,CORS仅支持客户端向服务器发送如下的9个请求头:
Accept、Accept-Language、 Content-Language、 DPR、 Downlink、 Save-Data、 Viewport-Width、 Width 、
Content- Type (值仅限于 text/plain、multipart/form-data、 application/x-www-form-urlencoded 三者之一)
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access- Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败!
1 //允许客户端额外向服务器发送Content-Type 请求头和X-Cus tom-Header 请求头
2 //注意:多个请求头之间使用英文的逗号进行分割
B res.setHeader( ' Access -Control-Allow-Headers',' Content - Type, X-Custom-Header' )
- CORS响应头部- Access-Control-Allow-Methods
默认情况下,CORS仅支持客户端发起GET、POST、HEAD请求。
如果客户端希望通过PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过Access-Control- Alow- Me来指明实际请求所允许使用的HTTP方法。
示例代码如下:
1 //只允许POST、 GET、 DELETE、 HEAD请求方法
res.setHeader('Access -Control-Allow-Methods', 'POST, GET, DELETE, HEAD')
//允许所有的HTTP 请求方法
res.setHeader('Access-Control-Al low-Methods', '*')
简单请求和预简请求
简单请求
同时满足以下两大条件的请求,就属于简单请求:
①请求方式: GET、POST、HEAD三者之一
②HTTP 头部信息不超过以下几种字段:无迫定义头部字段、Accept、 Accept-Language、 Content-Language、 DPR、
Downlink、Save-Data、 Viewport-Width、 Width 、Content- Type (只有三个值application/x-www-form-
urlencoded、multipart/form-data、 text/plain)
预检请求
只要符合以下任何一个条件的请求,都需要进行预检请求:
①请求方式为 GET、POST、 HEAD之外的请求Method塑
②请求头中包含自定义头部字段
③向服务器发送 了application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的OPTION请求称为"预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并携带真实数据。
简单请求和预检请求的区别
简单请求的特点:客户端与服务器之间只会发生-次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。
Node.js中CORS手动处理方法:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Authorization,X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method' )
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PATCH, PUT, DELETE')
res.header('Allow', 'GET, POST, PATCH, OPTIONS, PUT, DELETE')
next();
});
使用和下载cors包:
下载:npm install cors
调用:
const cors = require('cors')
app.use(cors())
我们这里需要注意的是在设置Origin字段时可以有两种情况:* 和一个域名
在使用*(接受所有域名)的时候浏览器为了安全考虑是不会发送Cookie值的,使用一个域名的时候是可以的。但是在实际开发中我们通常只需要使用一个域名就可以完成业务了。
总结
跨域手段还有很多,比如用nginx或者websocket,像CORS不同后端的代码写法也不同,博主也还未完全掌握,其它内容未来会持续更新。