跨域(扩展知识)
-
一个域名的组成:
协议、域名、端口、虚拟目录、文件目录
-
当协议、子域名、主域名、端口号中任意一个不同时,都算作不同域。
-
跨域:不同域之间互相请求资源
-
js出于安全考虑,不允许跨域调用其他页面的对象。
-
端口号默认80
-
注意区分HTTP和HTTPS
-
主域名确定,则它下面的子域名可以有多个,也可以多级。
例如:
主域名:abc.com
一级子域名:www.abc.com
二级子域名:xiantai.bbs.abc.com
三级子域名:haidian.beijing.bbs.abc.com
处理跨域方式(一):代理
所谓反向代理服务器,它是代理服务器中的一种。客户端直接发送请求给代理服务器,然后代理服务器会根据客户端的请求,从真实的资源服务器中获取资源返回给客户端。所以反向代理就隐藏了真实的服务器。利用这种特性,我们可以通过将其他域名的资源映射成自己的域名来规避开跨域问题。
nginx.conf.js // 配置跨域地址
处理跨域方式(二):JSONP
1.原理:ajax无法跨域是因为同源政策,但是带有src的标签(例如img、iframe、script)是不受该政策限制,故我们可以利用向页面动态添加script标签,来完成对跨域的访问
2.假设,我们源页面是在a.com,想要获取b.com的数据,我们可以动态插入
script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.b.com/getdata?callback=demo';
这里,我们利用动态脚本的src属性,变相地发送了一个http://www.b.com/getdata?callback=demo的GET请求。这时候,b.com页面接受到这个请求时,如果没有JSONP,会正常返回json的数据结果,像这样:
{ msg: 'helloworld' }
而利用JSONP,服务端会接受这个callback参数,然后用这个参数值包装要返回的数据:
demo({msg: 'helloworld'});
这时候,如果a.com的页面上正好有一个demo的函数:
function demo(data) {
console.log(data.msg);
}
当远程数据一返回的时候,随着动态脚本的执行,这个demo函数就会被执行。
到这里,你应该能明白这个技术为什么叫JSONP了吧?就是因为使用这种技术服务器会接受回调函数名作为请求参数,并将JSON数据填充进回调函数中去。
3.特点:
优点:兼容性好
缺点:(1)只能get不能post,因为script引入的资源,参数全都显式地url里 (2)安全隐患,因为动态插入<sciprt>
本质是脚本注入
4.封装
jQuery
$.ajax({
url: "http://tonghuashuo.github.io/test/jsonp.txt",
dataType: 'jsonp',
jsonp: "callback",
jsonpCallback: "dosomething"
})
.done(function(res) {
console.log("success");
console.log(res);
})
.fail(function(res) {
console.log("error");
console.log(res);
});
实际发送出来的完整请求长这样:
http://tonghuashuo.github.io/test/jsonp.txt?callback=dosomething&_=1471419449018。
,后面的随机字符串是jQuery加上的。
这里真正需要关心的参数有以下 3 个:
dataType: 'jsonp',用于表示这是一个 JSONP 请求。
jsonp: 'callback',用于告知服务器根据这个参数获取回调函数的名称,通常约定就叫 callback。
jsonpCallback: 'dosomething',回调函数的名称,也是前面callback参数的值。
其中jsonpCallback参数是可以省略的,jQuery 会自动生成一个随机字符串作为函数名,推荐这么做,以减少不必要的命名工作,同时排除潜在的安全隐患。
注意:省略jsonpCallback的同时,jsonp参数必须指明,不能为false。
js
<script>
// 实现回调函数,这里没有了 jQuery 的封装,必须手动指定并实现
var dosomething = function(data){
console.log(data);
};
// 提供 JSONP 服务的 URL 地址,查询字符串中加入 callback 指定回调函数
var url = "tonghuashuo.github.io/test/jsonp.txt?callback=docomething";
// 创建 <script> 标签,设置其 src 属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把 <script> 标签加入 <body> 尾部,此时调用开始。
document.getElementsByTagName('body')[0].appendChild(script);
// 因为目标 URL 是一个后台脚本,访问后会被执行,返回的 JSON 被包裹在回调函数中以字符串的形式被返回。
// 返回的字符串放入 <script> 中就成为了一个普通的函数调用,执行回调函数,返回的 JSON 数据作为实参被传给了回调函数。
</script>
是不是觉得没看够,来点干货,2333…
(1)资源类型文件可以实现跨域:当前网页可以查看其他域的image标签、script脚本文件、css等,其中原理是利用src属性或link的href属性。(image标签、script脚本文件、css属于资源类型文件。)
举个例子:
<script src="http://static.runoob.com/assets/vue/1.0.11/vue.min.js"></script>
常见的利用cdn引入库文件,便属于利用script标签实现跨域。
(2)利用高德地图举例:
(3)为何jsonp只能gt,不能post?
i>script跨域请求静态资源cdn,只能为get,不能为post
ii>当用户输入完url来访问网页时,便会对服务器发送请求,该请求属于通过get获取资源。若将资源替换成API(或说是回调),即可实现跨域。
处理跨域方式(三):XHR2
1.HTML5提供的XHR Level2已经实现了跨域访问,以及其他的新功能
2.IE10-独不支持XHR 2
3.在服务器端添加:
header('Access-Control-Origin:*');
header('Access-Control-Allow-Methods:POST,GET');
4.并非所有浏览器都实现了XHR2规范,但所有浏览器都实现了它规定的部分内容。
处理跨域方式(四):web sockets
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
1.)前端代码:
<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');
// 连接成功处理
socket.on('connect', function() {
// 监听服务端消息
socket.on('message', function(msg) {
console.log('data from server: ---> ' + msg);
});
// 监听服务端关闭
socket.on('disconnect', function() {
console.log('Server socket has closed.');
});
});
document.getElementsByTagName('input')[0].onblur = function() {
socket.send(this.value);
};
</script>
2.)Nodejs socket后台:
var http = require('http');
var socket = require('socket.io');
// 启http服务
var server = http.createServer(function(req, res) {
res.writeHead(200, {
'Content-type': 'text/html'
});
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
// 监听socket连接
socket.listen(server).on('connection', function(client) {
// 接收信息
client.on('message', function(msg) {
client.send('hello:' + msg);
console.log('data from client: ---> ' + msg);
});
// 断开处理
client.on('disconnect', function() {
console.log('Client socket has closed.');
});
});
处理跨域方式(五):CORS
1.在CORS中,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
2.浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
必须同时满足以下两大条件,才属于简单请求。否则属于非简单请求。
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息必须属于以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
3.对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。下面是一个例子,浏览器发现这次跨源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...
4.非简单请求
(1)非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
下面是这个"预检"请求的HTTP头信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
(2)服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
(3)一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
5.CORS比JSONP更强大JSONP只支持GET请求,CORS支持所有类型的HTTP请求;JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
处理跨域方式(六):图像ping
图像ping是与服务器进行简单,单项的跨域通信的一种方式
var img=new Image();
img.img.function(){
alert("done")
}
img.src="http://www.example.com/test?name=sl";
处理跨域方式(七):jquery
只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。
$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件
的形式来调用jsonp的回调函数。
$.getJSON(
"http://example.com/data.php?callback=?",
function (jsondata){
// 回调事件
});
处理跨域方式(八):iframe
只有在主域相同时,才能使用该方法
处理跨域方式(九):window.name+iframe
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
处理跨域方式(十):window.postMesssage+iframe
postMessage是HTML5新增在window对象上的方法,目的是为了解决在父子页面上通信的问题。该技术有个专有名词:跨文档消息(cross-document messaging)。利用postMessage的特性可以实现较为安全可信的跨域通信。
postMessage方法接受两个参数:
message
: 要传递的对象,只支持字符串信息,因此如果需要发送对象,可以使用JSON.stringify和JSON.parse做处理
targetOrigin
: 目标域,需要注意的是协议,端口和主机名必须与要发送的消息的窗口一致。如果不想限定域,可以使用通配符“*”,但是从安全上考虑,不推荐这样做。
处理跨域方式(十一):Window.loacationhhash+iframe
location.href可以获取url中“#”后面的值,并且改变hash值不会导致页面刷新,所以可以利用hash值来进行数据的传递,当然数据量有限。
缺点:(1)数据直接暴漏在url中 (2)数据容量和类型都有限
处理跨域方式(十二):document.domain
如,来自www.a.com想要获取document.a.com中的数据。只要基础域名相同,便可以通过修改document.domain为基础域名的方式来进行通信,但是需要注意的是协议和端口也必须相同。
处理跨域方式(十三) webpack配置
1.vue : vue.config.js文件,设置proxy
2.webpack.config.js
devServer:{
proxy:"xxx",
changeOrigin:"true"
}
处理跨域方式(十四) koa-cors
处理跨域方式(十五) 请求封装
axios中,proxy参数
处理跨域方式(十六) 中间件
http-proxy-middleware
小结:结合场景选择不同解决方式啦,个人常用jsonp、请求头、webpack.config.js。欢迎指正~