跨源网络访问
链接:浏览器的同源策略
链接:跨域资源共享
链接:跨域共享数据的十种方法
链接:前端跨域问题及其解决方案
广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: <link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
跨源网络访问特点 | |
---|---|
允许跨域写、资源嵌入 | 写:(链接links ,重定向以及表单提交...)。内嵌资源(可读取嵌入图片的高度和宽度...) |
不允许跨域读 |
可用于嵌入跨源的标签
标签 | |
---|---|
script 跨域脚本 |
语法错误信息只能在同源脚本中捕捉到 |
link css |
CSS跨域需要设置正确的Content-Type 消息头 |
img 嵌入图片 |
图片格式包括PNG,JPEG,GIF,BMP,SVG,... |
video 、audio
|
多媒体资源 |
object 、embed 、applet
|
插件 |
@font-face |
浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts) |
frame 、iframe
|
可载入任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。 |
... | ... |
跨域资源共享(cross-origin sharing)
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST
请求),浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
CORS | |
---|---|
方式 | 如:XMLHttpRequest、Fetchs使用CORS,以降低跨域HTTP请求所带来啊的风险 |
限制 | CORS需要客户端和服务端同时支持 |
共享范围 | XMLHttpRequest、Fetch发起的HTTP请求。Web字体@font-face 。WebGL贴图。媒体资源画面绘制Images/video绘制到canvas 。样式表。Scripts |
常见跨域方案
方案 | |
---|---|
JSONP跨域 | jsonp是利用script标签可嵌入跨源的特性。嵌入的资源为js脚本(实际是动态对上文存在函数的实例化) ,仅支持get |
Flash URLLoader | 通过flash发送HTTP请求(iOS不支持) |
cross-origin sharing(CORS) | 服务端设置白名单Access-Control-Allow-Origin: [域名白名单] ,客户端通过XMLHTTPRequest /Fetch/XDomainRequest(IE8) 请求 |
server proxy | nginx/nodejs/apache...代理 |
document.domain + iframe | 父子域,兄弟域场景。设置document.domain为同样值(或基础主域) |
location.hash + iframe FIM – Fragment Identitier Messaging
|
a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信 |
window.name + iframe | name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB) |
postMessage | 页面与新窗口,多窗口之间,页面与iframe信息交互 |
WebSocket协议跨域 | 浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现 |
Flash LocalConnection | SWF之间通过进程通信 |
Cross Frame | Cross Frame是FIM的一个变种,它借助了一个空白的iframe |
JSONP实际是动态对上文存在函数的实例化
<script>
function setTitle(title) {
return title ? document.querySelector('title').innerHTML = title : 0
}
function setName(name) {
return name ? document.getElementById('name').innerHTML = name : 0
}
...
</script>
<script src="https://www.jianshu.com/...."></script> // 异步返回setTitle('跨源网络访问') ,setName('可返回N多个') ......
document.domain + iframe 解决跨域
1.)父窗口:(http://www.domain.com/a.html)<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>复制代码2.)子窗口:(http://child.domain.com/b.html)<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>
这种方式适用于主域相同,子域不同,比如http://www.a.com和http://b.a.com
假如这两个域名下各有a.html 和b.html,
a.html
document.domain = "a.com";
var iframe = document.createElement("iframe");
iframe.src = "http://b.a.com/b.html";
document.body.appendChild(iframe);
iframe.onload = function() {
console.log(iframe.contentWindow....); // 在这里操作b.html里的元素数据
}
b.html
document.domain = "a.com";
注意:document.domain需要设置成自身或更高一级的父域,且主域必须相同。
postMessage
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:a.) 页面和其打开的新窗口的数据传递b.) 多窗口之间消息传递c.) 页面与嵌套的iframe消息传递d.) 上面三个场景的跨域数据传递用法:postMessage(data,origin)方法接受两个参数data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
WebSocket协议跨域
WebSocket协议跨域WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。原生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.');
});
});
跨源访问
同源策略控制了不同源之间的交互,例如在使用XMLHttpRequest
或 <img>
标签时则会受到同源策略的约束。这些交互通常分为三类:
- 通常允许跨域写操作(Cross-origin writes)。例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加 preflight。
- 通常允许跨域资源嵌入(Cross-origin embedding)。之后下面会举例说明。
- 通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问。例如可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或availability of an embedded resource.
以下是可能嵌入跨源的资源的一些示例:
-
<script src="..."></script>
标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。 -
<link rel="stylesheet" href="...">
标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type
消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。 -
<img>
嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,... -
<video>
和<audio>
嵌入多媒体资源。 -
<object>
,<embed>
和[<applet>](https://developer.mozilla.org/zh-CN/docs/HTML/Element/applet "HTML/Element/applet")
的插件。 -
[@font-face](https://developer.mozilla.org/zh-CN/docs/CSS/@font-face "CSS/@font-face")
引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。 -
<frame>
和[<iframe>](https://developer.mozilla.org/zh-CN/docs/HTML/Element/iframe "HTML/Element/iframe")
载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。
跨源数据存储访问
存储在浏览器中的数据,如localStorage和IndexedDB,以源进行分割。每个源都拥有自己单独的存储空间,一个源中的Javascript脚本不能对属于其它源的数据进行读写操作。
Cookies 使用不同的源定义方式。一个页面可以为本域和任何父域设置cookie,只要是父域不是公共后缀(public suffix)即可。Firefox 和 Chrome 使用 Public Suffix List 决定一个域是否是一个公共后缀(public suffix)。Internet Explorer使用其自己的内部方法来确定域是否是公共后缀。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains) 访问 cookie。设置 cookie 时,你可以使用Domain,Path,Secure,和Http-Only标记来限定其访问性。读取 cookie 时,不会知晓它的出处。 即使您仅使用安全的https连接,您看到的任何cookie都可能使用不安全的连接进行设置。
如何允许跨源访问
使用 CORS 允许跨源访问。
如何阻止跨源访问
- 阻止跨域写操作,只要检测请求中的一个不可测的标记(CSRF token)即可,这个标记被称为Cross-Site Request Forgery (CSRF) 标记。必须使用这个标记来阻止页面的跨站读操作。
- 阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。
- 阻止跨站嵌入,需要确保你的资源不能是以上列出的可嵌入资源格式。多数情况下浏览器都不会遵守
Conten-Type
消息头。例如,如果您在HTML文档中指定<script>
标记,则浏览器将尝试将HTML解析为JavaScript。 当您的资源不是您网站的入口点时,您还可以使用CSRF令牌来防止嵌入。
跨源脚本API访问
avascript的APIs中,如 iframe.contentWindow
, window.parent
, window.open
和 window.opener
允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对 Window 和 Location对象的访问添加限制,如下两节所述。
为了在不同源中文档进一步交流,可以使用window.postMessage
。
Window
规范: http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#security-window.
允许以下对 Window
属性的跨源访问:
方法:
window.blur
window.close
window.focus
window.postMessage
属性 | |
---|---|
window.closed |
只读. |
window.frames |
只读. |
window.length |
只读. |
window.location |
读/写. |
window.opener |
只读. |
window.parent |
只读. |
window.self |
只读. |
window.top |
只读. |
window.window |
只读. |
Location
Specification: http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#security-location.
允许以下对 Location
属性的跨源访问:
location.replace
URLUtils.href