1:什么是同源策略及限制
1-1:什么是同源?
如果两个URL的协议、域名和端口号都相同,我们就称这两个URL同源。
1-2:什么是同源策略
浏览器默认两个相同的源之间是可以相互访问资源和操作DOM的。而不同的源之间不可以做到这一点,若想要相互访问资源或者操作DOM,那么需要增加一些安全策略的制约,我们把这称为同源策略。
1-3:不可以相互访问资源和操作DOM分别都是指什么?
可以分为三个部分:DOM层面、数据层面、网络层面
1-3-1- DOM层面
不同源的JS脚本不可以对当前站点的DOM对象进行读写操作 如,通过A服务器发来的JS脚本去修改B服务器中某个网页的HTML内容,是做不到的。
1-3-2- 数据层面
不同源的站点不可以读取当前站点的Cookie、IndexDB、LocalStorage等数据
1-3-2- 网络层面
限制通过http请求(GET、POST等)将某站点的数据发送给不同源的站点。反之,不可以通过http请求向不同源站点获取数据。
1-4、如果突破这三个层面的限制?
- 读取数据和操作DOM用跨文档机制
- 跨域请求用CORS机制
- 引用第三方资源用CSP
同源策略限制
从一个源加载的文档或者脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的关键的安全机制。
cookie、localStorage和indexDB无法读取
DOM无法获得
AJAX请求不能发送
2:前后端如何通讯
1. ajax
浏览器发起请求,服务器返回数据,服务器不能主动返回数据,要实现实时数据交互只能是ajax轮询(让浏览器隔个几秒就发送一次请求,然后更新客户端显示。这种方式实际上浪费了大量流量并且对服务端造成了很大压力)。
2. websocket
websocket是HTML5出的东西(协议),是一种全双工通信机制,两端可以及时地互发事件,互发数据,相互通信,只需要浏览器和服务器建立一次连接,服务器就可以主动推送数据到浏览器实现实时数据更新。
原生 websocket 支持到IE11 ,实际开发中,有比较著名的两个库socket.io(推荐)(英文版、中文版可能跟新不及时) 和 sockjs , 它们都对原始的API做了进一步封装和兼容IE,提供了更多功能,都分为客户端和服务端的实现,实际应用中,可以选择使用。
websocket 的实现需要后端搭建一个WebSocket服务器,但是如果想搭建一个WebSocket服务器就没有那么轻松了,因为WebSocket是一种新的通信协议,目前还是草案,没有成为标准,市场上也没有成熟的WebSocket服务器或者Library实现WebSocket协议,我们就必须自己动手写代码去解析和组装WebSocket的数据包。要这样完成一个WebSocket服务器,估计所有的人都想放弃,幸好的是市场上有几款比较好的开源库供我们使用,比如 PyWebSocket,WebSocket-Node, LibWebSockets等等,这些库文件已经实现了WebSocket数据包的封装和解析,我们可以调用这些接口,这在很大程度上减少了我们的工作量。
3:socket.io(推荐)
socket.io 是一个为实时应用提供跨平台实时通信的库。socket.io 旨在使实时应用在每个浏览器和移动设备上成为可能,模糊不同的传输机制之间的差异。
socket.io 的名字源于它使用了浏览器支持并采用的 HTML5 WebSocket 标准,因为并不是所有的浏览器都支持 WebSocket ,所以该库支持一系列降级功能:
- Websocket
- Adobe® Flash® Socket
- AJAX long polling
- AJAX multipart streaming
- Forever Iframe
- JSONP Polling
在大部分情境下,你都能通过这些功能选择与浏览器保持类似长连接的功能。
- 优点:跨平台、兼容性好、具有降级功能、所有传输机制接口对外统一、自带心跳。
- 缺点:要使用socket.io必须前后端都要用一套框架。
- 适用于:考虑更多兼容性,后端可以使用基于socket.io的框架的情景。(常见服务端实现框架有node.js,Netty-socket.io)
3:如何创建ajax
Ajax的原理:
简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据
ajax过程:
3-1:创建 XMLHttpRequest 对象,也就是创建一个异步调用对象
3-2:创建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、URL 及验证信息
3-3:设置响应 HTTP 请求状态变化的函数
3-4:发送 HTTP 请求
3-5:获取异步调用返回的数据
3-6:使用 JavaScript和 DOM 实现局部刷新
4:跨越通讯的几种方式
4-1:JSONP
支持跨域,本质是script标签不受同源策略的限制。只支持GET请求。
以下是功能比较完整的jsonp代码
var count = 0;
function noop () {}
function jsonp(url, opts, fn){
if (!opts) opts = {};
var prefix = opts.prefix || '__jp';
var id = opts.name || (prefix + (count++));
var param = opts.param || 'callback';
var timeout = null != opts.timeout ? opts.timeout : 60000;
var target = document.getElementsByTagName('script')[0] || document.head;
var script;
var timer;
if (timeout) {
timer = setTimeout(function(){
cleanup();
if (fn) fn(new Error('Timeout'));
}, timeout);
}
function cleanup(){
if (script.parentNode) script.parentNode.removeChild(script);
window[id] = noop;
if (timer) clearTimeout(timer);
}
function cancel(){
if (window[id]) {
cleanup();
}
}
window[id] = function(data){
cleanup();
if (fn) fn(null, data);
};
url += (url.includes('?') ? '&' : '?') + param + '=' + encodeURIComponent(id);
url = url.replace('?&', '?');
script = document.createElement('script');
script.src = url;
target.parentNode.insertBefore(script, target);
return cancel;
}
4-2:CORS
可以将其理解为支持跨域的Ajax。具体可以参考阮一峰老师的文章。
4-3:WebSocket
支持跨域。使用ws://(非加密)和wss://(加密)作为协议前缀。
4-4:图像PING
同样支持跨域,但是只支持GET请求并且无法得到返回结果。
(new Image()).src = 'http://xxx.cn?id=3'
4-5:Hash
用于页面A与其iframe中的跨域页面B进行通信,B也可以改变A的Hash。
// A页面
bPage.src = bPage.src + '#' + data
// B页面
window.onhashchange = function () {
const data = window.location.hash
}
4-6:postMessage
HTML5推出的新API。
// 父窗口
childWindow.postMessage('Hello World!', 'http://bbb.com')
// 子窗口
window.addEventListener('message', function (e) {
e.origin // 父窗口的域名
e.source // 父窗口的window对象
e.data // 传输的数据
})
4-7:document.domain
适用于 Cookie 和 iframe 窗口。若两个网页一级域名相同,只是二级域名不同,则能通过设置相同的document.domain进行跨域通信。