跨域技术(上)包括 同源策略及其限制,什么是跨域,图像Ping、JSONP、CORS、WebSocket 等跨域技术。php
所谓同源是指:协议,域名,端口都相同,就是同源;前端
协议,域名,端口号有一个不一样就是跨域。nginx
跨域并非请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。 git
<script src="..."></script>
| 标签嵌入跨域脚本。github
<link rel="stylesheet" href="...">
| 标签嵌入CSS。web
<img>
| 嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...ajax
<video>
和 <audio>
| 嵌入多媒体资源。json
<object>
, <embed>
和 <applet>
的插件。segmentfault
<frame>
和 <iframe>
| 载入的任何资源。站点可使用X-Frame-Options消息头来阻止这种形式的跨域交互。
@font-face
引入的字体 | 一些浏览器容许跨域字体(cross-origin fonts),一些须要同源字体(same-origin fonts)。
background: url();
背景连接 | 嵌入的资源可能存在跨域
<link>
、<script>
、<img>
、<frame>
等dom标签1.原理
onload
和onerror
事件处理程序来肯定是否接收到了响应。2.具体步骤
请求的数据是经过查询字符串形式发送的,而响应能够是任意内容,但一般是像素图或 204 响应。经过图像 Ping,浏览器得不到任何具体的数据,但经过侦听 load 和 error 事件,它能知道响应是何时接收到的。
let img = new Image(); //建立了一个 Image 的实例
// 将 onload 和 onerror 事件处理程序指定为同一个函数
img.onload = img.onerror = function(){
alert("Done!");
};
// 请求中发送了一个 name 参数
img.src = "http://www.example.com/test?name=Nicholas";
复制代码
3.应用场景
图像 Ping 最经常使用于跟踪用户点击页面或动态广告曝光次数。
4.图像 Ping 的缺点
1.JSONP的做用、组成?
2.JSONP实现跨域访问的原理及优化
2.1 <script>
标签的做用
<script>
标签,同一个界面中多个<script>
标签中的数据能够相互访问<script>
的src属性导入其它资源,经过src属性导入其它资源的本质就是将资源拷贝到<script>
标签中<script>
的src属性不只能导入本地资源, 还能导入远程资源<script>
的src属性没有同源限制, 因此能够经过<script>
的src属性来请求跨域数据2.2 JSONP原理
JSONP 是经过动态<script>
元素来使用的,使用时能够为 src 属性指定一个跨域 URL。这里的<script>
元素与<img>
元素相似,都有能力不受限制地从其余域加载资源。由于 JSONP 是有效的 JavaScript代码,因此在请求完成后,即在 JSONP 响应加载到页面中之后,就会当即执行。
2.3 JSONP优化
2.3.1 为何要优化(存在的问题)?
<script>
标签默认是同步,前面的<script>
标签没有加载完数据,后面的<script>
标签就不会被执行, 因此请求数据的<script>
标签必须放到后面2.3.2 解决方案:
<script>
标签,由于JS动态建立的<script>
标签默认就是异步的, 不用等到前面的标签加载完就能够执行后面的<script>
标签2.3.3 实现方式
原生实现
<script>
//jsonp回调方法,必定要写在jsonp请求前面
function testjson(txt){
alert(txt);
}
</script>
<script>
//动态建立<script>标签
let oScript = document.createElement("script");
oScript.src = "http://127.0.0.1:80/jQuery/Ajax/20-jsonp.php?cb=test";
document.body.appendChild(oScript);
</script>
复制代码
jQuery方式实现
jQuery中的Ajax能够设置为请求跨域的数据
dataType: "jsonp"
| 设置请求方式为jsonp,告诉jQuery须要请求跨域的数据。jsonp: "cb"
| 告诉jQuery服务器在获取回调函数名称的时候须要用什么key来获取jsonpCallback: "handleCallback"
| 自定义回调函数名,告诉jQuery服务器在获取回调函数名称的时候回调函数的名称是什么<script>
$.ajax({
url: "http://127.0.0.1:80/jQuery/Ajax/22-jsonp.php",
data:{
"name": "ghk",
"age": 18
},
dataType: "jsonp", // 设置请求方式为jsonp
jsonp: "cb",
jsonpCallback: "handleCallback", // 自定义回调函数名
success: function (msg) {
console.log(msg);
}
});
</script>
复制代码
3.JSONP的优缺点
3.1 优势
与图像 Ping 相比,它的优势在于可以直接访问响应文本,支持在浏览器与服务器之间双向通讯。
3.2 缺点
1.CORS是什么及原理
一句话总结:CORS是跨域的根本解决方法(处理跨域问题的标准作法),由浏览器自动完成。
CORS是一个W3C标准,它容许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS背后的基本思想:使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,仍是应该失败。
整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。对于开发者来讲,CORS通讯与同源的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感受。
所以,实现CORS通讯的关键是服务器。只要服务器实现了CORS接口,就能够跨源通讯。
2.CORS的两种请求
浏览器将CORS请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时知足如下两大条件,就属于简单请求。
(1) 请求方法是如下三种方法之一:
(2) HTTP的头信息不超出如下几种字段:
凡是不一样时知足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不同的。(阮老师这里有对这两种请求的详细解说)
3.实现
普通跨域请求:服务端设置Access-Control-Allow-Origin便可,前端无须设置;若要带cookie请求,先后端都须要设置。
在响应头上添加 Access-Control-Allow-Origin 属性,指定同源策略的地址。同源策略默认地址是网页的自己。只要浏览器检测到响应头带上了CORS,而且容许的源包括了本网站,那么就不会拦截请求响应。
原生实现
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;// 前端设置是否带cookie
...
复制代码
jQuery Ajax
$.ajax({
...
xhrFields: {
withCredentials: true, // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});
复制代码
4.CORS优缺点
CORS要求浏览器(>IE10)和服务器的同时支持,是跨域的根本解决方法,由浏览器自动完成。 优势在于功能更增强大支持各类HTTP Method,缺点是兼容性不如JSONP。
同源策略对 WebSocket 不适用,所以能够经过它打开到任何站点的链接。
1.WebSocket的由来
为了解决服务器向浏览器端及时推送消息的需求,提出了一些替代的、变通的解决方案,如iframe、AJAX,基本思路是浏览器端及时提交请求,服务器收到请求后进行阻塞,并保持链接,等待有消息须要发送时,及时向浏览器端进行响应推送。 然而,这些技术都是基于HTTP协议实现。从根本上讲,HTTP是半双工的协议,也就是说,在同一时刻信息只能单向传输。浏览器端向服务器发送请求(单向),而后服务器响应请求(单向)。这样,服务器只能被动的接收到请求信息后,再向浏览器端响应请求,而没法主动向客户端推送信息。
然而,随着人们对信息的实时性要求愈来愈高(如在线订票系统、即时通讯系统和股票交易系统等),当服务器数据发生变化的时候,须要主动、实时地向浏览器端发送消息,将最新的数据或事件通知给用户。显然,HTTP做为半双工的通讯协议并不能高效地支持上述需求功能的实现。咱们须要一种高效节能的全双工通讯机制来保证数据的实时传输。所以,WebSocket应运而生。
2.WebSocket的优势和应用场景
2.1 WebSocket的优势
WebSocket做为一种高效的全双工通讯机制,有以下优势
2.2 应用场景
经过WebSocket实现的浏览器后台传输技术,能够应用于如多媒体聊天、实时状态监控、股票行情、基于位置的应用等须要实时刷新的应用场景。
3.WebSocket协议通讯机制
WebSocket协议是独立的、基于TCP的协议。其本质是先经过HTTP/HTTPS协议进行握手后建立一个用于交换数据的TCP链接,此后服务器端与浏览器端经过此TCP链接进行实时通讯。
图所示为WebSocket的通讯模式:
浏览器端首先向服务器端发起一个HTTP请求。这个请求包含了一些附加头信息,是一个升级的HTTP请求。服务器端解析这些附加头信息后,产生应答信息返回给浏览器端。这样,浏览器端和服务器端之间的WebSocket链接就创建起来了。双方能够经过这个链接通道自由地传输数据,直到浏览器端或服务器端主动关闭该链接。
4.WebSocket协议通讯实现的相关技术
4.1 WebSocket构造函数
4.1.1 客户端要和服务器端创建WebSocket链接,只需使用构造函数创建一个WebSocket对象,而且为这个对象提供链接端口的URL地址便可。
let ws = new WebSocket("ws://127.0.0.1:8080/WebSocket/WebSocket")
复制代码
4.1.2 注意点
URL地址的字符串须要以“ws”或“wss”(加密通讯时使用)做为开头。若是URL地址有语法错误,那么构造函数将会抛出异常。
必须给 WebSocket 构造函数传入绝对 URL。同源策略对 Web Sockets 不适用,所以能够经过它打开到任何站点的链接。至因而否会与某个域中的页面通讯,则彻底取决于服务器。
4.2 WebSocket的readyState属性
readyState
属性返回实例对象的当前状态,共有四种。
状态 | 含义 |
---|---|
WebSocket.OPENING (0) | 正在创建链接。 |
WebSocket.OPEN (1) | 已经创建链接。 |
WebSocket.CLOSING (2) | 正在关闭链接。 |
WebSocket.CLOSE (3) | 已经关闭链接。 |
4.3 WebSocket事件
open
事件(WebSocket链接创建后触发该事件)
一旦服务器响应了客户端的WebSocket链接请求,open事件就会被触发。而要监听open事件,只须要为其添加回调函数。
ws.onopen = function(){
setConnected(true));
};
复制代码
message
事件(当客户端接收到服务器的数据时触发该事件)
当客户端接收到服务器发送的消息后,message事件触发,而且客户端可调用相应函数对所接收的消息进行处理。
ws.onmessage= function(e){
console.log("接收:"+e.data);
}
复制代码
error
事件(当通讯过程当中发生错误时触发该事件)
error事件会在WebSocket链接出现故障时触发,用来处理异常事件。
ws.onerror = function(e){
console.log("WebSocket Error: ", e);
};
复制代码
close
事件(当链接关闭时触发该事件)。
若是接收到error事件,能够预期很快会触发close事件。close事件在WebSocket链接关闭时触发,处理程序以下所示:
ws.onclose = function(e){
setConnected(false));
};
复制代码
一旦链接关闭,客户端和服务器再也不接收或者发送消息。
4.4 WebSocket方法
WebSocket对象有两个方法即send()和close(),分别用来发送数据和关闭链接。
send()
方法ws.send("message");
复制代码
close()
方法ws.close(code, reason);
复制代码
能够在close()方法传递两个可选参数:code(数字型的状态代码)和reason(一个文本字符串),用来讲明关闭链接的缘由。5.WebSocket小结
有没有发现好用的东西、牛逼的东西总得要有点小缺陷才行,例如兼容问题,否则开发就没啥意思了(不是
JavaScript 中建立了 WebSocket 以后,会有一个 HTTP 请求发送到浏览器以发起链接。在取得服务器响应后,创建的链接会使用 HTTP 升级从 HTTP 协议交换为 Web Socket 协议。
也就是说,使用标准的 HTTP 服务器没法实现 WebSocket ,只有支持这种协议的专门服务器才能正常工做
目前支持WebSocket的服务器有不少,包括了Jetty七、Netty、mod pyWebSocket、Nodejs和Tomcat7等。在双向通道创建后,服务器端一样采用事件驱动的架构模式,监听事件、并触发相应方法函数来向客户端推送数据。
本文参考书籍:
《JavaScript高级程序设计》
本文参考连接:
跨域技术(下)包括 iframe标签的使用 和 document.domain、window.name、location.hash、postMessage等跨域技术。
域名地址的组成
iframe是一种HTML标记,它会建立包含另一个文档的内联框架,经过iframe框架能够在当前页面中显示其余页面的信息。
将iframe的src属性设置为对另一个页面的链接请求,并在当前页面中经过JavaScript动态更新iframe的内容,就能够将服务器端的数据响应到客户端且而不会出现主页面一片空白,等待刷新的现象。
而且,仅刷新iframe框架而不是主页面,也减小了页面刷新的内容,这在必定程度上提升了页面刷新速度。
iframe 标签的使用
1.用iframe
嵌套页面时,若是父页面要获取子页面里面的内容,可使用 contentWindow
或者contentDocument
子页面获取父页面使用window.parent
//父窗口
<iframe id="father" src="iframe.html"></iframe>
<script>
console.log(iframe.contentWindow); //获取子页面里面的内容
console.log(iframe.contentDocument);
</script>
//子窗口
<div id="son">内容</div>
<script>
console.log(window.parent); //获取父页面内容
</script>
复制代码
2.页面间通讯,获取子页面的内容、cookie等
//父窗口
<iframe id="father" src="iframe.html"></iframe>
<script>
let iframe = document.getElementById("father");
iframe.onload = function(){
let doc = iframe.contentWindow.document;
console.log(doc.getElementById('son').innerHTML);//'内容'
console.log(document.cookie);//'name=match'
}
</script>
//子窗口
<div id="son">内容</div>
<script>
document.cookie = 'name=match';
</script>
复制代码
这种方式只适合主域名相同,子域名不一样的iframe跨域。
经过修改document的domain属性,咱们能够在域和子域或者不一样的子域之间通讯。同域策略认为域和子域隶属于不一样的域,好比www.a.com和sub.a.com是不一样的域,这时,咱们没法在www.a.com下的页面中调用sub.a.com中定义的JavaScript方法。可是当咱们把它们document的domain属性都修改成a.com,浏览器就会认为它们处于同一个域下,那么咱们就能够互相调用对方的method来通讯了。
1.主域相同而二级域名不一样的状况下,两个文件分别加上 document.domain = "example.com"
;而后经过 a.html 文件建立一个 iframe,去控制 iframe 的 window,从而进行交互,这种方法只能解决主域相同而二级域名不一样的状况。
2.注意
使用document.domain容许子域安全访问其父域时,您须要设置document域在父域和子域中具备相同的值。这是必要的,即便这样作只是将父域设置回其原始值。不然可能会致使权限错误。这里都是a.com。
// 在www.a.com/a.html中:
<script>
document.domain = 'a.com';
let ifr = document.createElement('iframe');
ifr.src = 'http://www.script.a.com/b.html';
ifr.display = none;
document.body.appendChild(ifr);
ifr.onload = function(){
let doc = ifr.contentDocument || ifr.contentWindow.document;
ifr.onload = null;
};
</script>
复制代码
// 在www.script.a.com/b.html中:
<script>
document.domain ='a.com';
window.data = '传送的数据:1111';
</script>
复制代码
window.name
属性用于获取/设置窗口的名称。该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的全部的页面都是共享一个window.name的,每一个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的全部页面中的。window.name属性的神奇之处在于name值在不一样的页面(甚至不一样域名)加载后依旧存在(若是没修改则值不会变化),而且能够支持很是长的 name 值(2MB)。
当该window的location变化,而后从新加载,它的name属性能够依然保持不变。
在控制台输入:
能够将跨域请求用以下步骤解决:
window.name 的优点在于巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操做。
location.hash就是锚点值,又称为片断标识符(fragment identifier),指的是URL的#号后面的部分。
若是只是改变片断标识符,页面不会从新刷新 ,因此能够利用hash值来进行数据的传递,固然数据量是有限的。
1.假设 github.io 域名下 a.html 和 mouyuming.eu 域名下 b.html 存在跨域请求,那么利用 location.hash 的一个解决方案以下:
以上步骤中须要注意第二点:如何在 iframe 页面中修改 父亲页面的 hash 值。因为在 IE 和 Chrome 下,两个不一样域的页面是不容许 parent.location.hash 这样赋值的,因此对于这种状况,咱们须要在父亲页面域名下添加另外一个页面来实现跨域请求,具体以下:
2.location.bash 方法的优缺点
优势在于能够解决域名彻底不一样的跨域请求,而且能够实现双向通信。
而缺点则包括如下几点:
HTML5 为了跨域问题,引入了一个全新的 API:跨文档通讯 API(Cross-document messaging)。这个 API 为 window 对象新增了一个 window.postMessage 方法,容许跨窗口通讯,不论这两个窗口是否同源。
图所示为postMessage的兼容性:
格式:otherWindow.postMessage(message, targetOrigin, [transfer]);
参数:
otherWindow
| 其余窗口的一个引用,好比iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
message
| 将要发送到其余 window的数据;是具体的信息内容。
targetOrigin
| 接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也能够设为*,表示不限制域名,向全部窗口发送。
若是你明确的知道消息应该发送到哪一个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将致使数据泄露到任何对数据感兴趣的恶意站点。
2.父窗口和子窗口均可以经过message事件,监听对方的消息。
message 的属性有:
data
| 从其余 window 中传递过来的对象。(消息内容)
origin
| 调用 postMessage 时消息发送方窗口的 origin。(消息发向的网址)
source
| 对发送消息的窗口对象的引用。(发送消息的窗口)
//发送页面
<body>
<iframe id="myIframe" src="http://localhost:3000/user/reg"></iframe>
<input type="button" value="点我" onclick="test()">
<script>
function test() {
let frm = document.getElementById("myIframe");
frm.contentWindow.postMessage("跨域请求信息","http://localhost:3000");
}
</script>
</body>
// 接收页面
<script>
//接收信息页面 http://localhost:3000/parent.html
window.addEventListener("message",function (e) {
console.log(e.data);
},false);
</script>
复制代码
NodeJS中间件代理跨域、nginx反向代理中设置、flash等第三方插件等
本文参考连接