具体概念如下:只要协议、域名、端口有任何一个不同,都被当作是不同的域。
凡是拥有 “src” 这个属性的标签都有跨域的能力
跨域
浏览器请求(js,css,图片,form 表单提交)和 Ajax 请求都携带 Cookie 信息。但浏览器规定,静态资源请求和提交表单不受同源政策的限制,Ajax 请求受同源策略限制。
如果是协议和端口造成的跨域问题。则前台无法解决。 同源策略具体分为以下几类:
- 不同域名
- 相同域名不同端口号,如
https://www.oschina.net:8000
和https://www.oschina.net:8001
- 同一个域名不同协议,如
http://www.oschina.net/
和https://www.oschina.net/
- 域名和域名对应的的 IP,如
http://b.qq.com/
和http://10.198.7.85
- 主域和子域,如
http://www.oschina.net/
和https://test.oschina.net
- 子域和子域,如
https://test1.oschina.net
和https://test2.oschina.net
以上情况只要出现了,那么就会产生跨域问题。那么如果解决跨域问题呢,下面的小节会总结一些解决跨域常用的方法。
JSONP 带 callback 的 json
JSONP:只支持 GET,不支持 POST 请求代理
原理:浏览器只对 XHR(XMLHttpRequest) 请求有同源限制,二队 script 标签的 src 属性、link 的 ref 和 img 的 src 属性没有这种限制,利用这个就可以解决跨域请求问题
有个通俗易懂的解释 -JSONP(JSON with Padding )是数据格式 JSON 的一种 “ 使用模式 ”,可以让网页从别的网域要数据。利用<script>
标签没有跨域限制,来达到与第 3 方通讯的目的。
** jsonp 的客户端具体实现:**
- 远程服务器 remoteserver.com 根目录下有个 remote.js 文件代码如下:
1 | alert('我是远程文件') |
- 本地服务器 localserver.com 下有个 jsonp.html 页面代码如下:
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
页面将会弹出一个提示窗体,显示跨域调用成功。
- 现在我们在 jsonp.html 页面定义一个函数,然后在远程 remote.js 中传入数据进行调用。
jsonp.html 页面代码如下:
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
- remote.js 文件代码如下:
1 | localHandler({ result: '我是远程js带来的数据' }) |
要注意的是他支持 GET 这一种 HTTP 请求类型
跨域资源共享(CORS-Cross Origin Resource Sharing )
跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。
CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。浏览器 CORS 请求分成两种:
- 简单请求
- 协商模型 / 预检请求(Preflighted Request ),即非简单请求如何区分请求具体属于哪一种呢,下面我总结了几点:
- 请求方式:
- GET
- HEAD
- POST
- HTTP 的头信息子段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data 、 text/plain,其中 ‘text/plain’ 默认支持,其他两种则需要预检请求和服务器协商。
满足以上两大点的即为简单请求,否则为非简单请求。
假如站点 http://foo.example
的网页应用想要访问 http://bar.other
的资源。http://foo.example
的网页中可能包含类似于下面的 JavaScript 代码:
1 | var invocation = new XMLHttpRequest() |
document.domain+iframe (适用于主域名相同的情况)
比如,有一个页面,它的地址是http://www.damonare.cn/a.html , 在这个页面里面有一个 iframe,它的 src 是http://damonare.cn/b.html, 很显然,这个页面与它里面的 iframe 框架是不同域的,所以我们是无法通过在页面中书写 js 代码来获取 iframe 中的东西的:
1 | <script type="text/javascript"> |
这时只要把http://www.damonare.cn/a.html
和http://damonare.cn/b.html
这两个页面的 document.domain 都设成相同的域名就可以了。但要注意的是,document.domain 的设置是有限制的,我们只能把 document.domain 设置成自身或更高一级的父域,且主域必须相同。
- 在页面
http://www.damonare.cn/a.html
中设置 document.domain:
1 | <iframe id = "iframe" src="http://damonare.cn/b.html" onload = "test()"></iframe> |
- 在页面
http://damonare.cn/b.html
中也设置 document.domain:
1 | <script type="text/javascript"> |
修改 document.domain 的方法只适用于不同子域的框架间的交互。
通过 location.hash 跨域
此方法的原理就是改变 URL 的 hash 部分来进行双向通信。每个 window 通过改变其他 window 的 location 来发送消息(由于两个页面不在同一个域下 IE、Chrome 不允许修改 parent.location.hash 的值,所以要借助于父窗口域名下的一个代理 iframe),并通过监听自己的 URL 的变化来接收消息。这个方式的通信会造成一些不必要的浏览器历史记录,而且有些浏览器不支持 onhashchange 事件,需要轮询来获知 URL 的改变,最后,这样做也存在缺点,诸如数据直接暴露在了 url 中,数据容量和类型都有限等。
通过 HTML5 的 postMessage 方法跨域
这个功能主要包括接受信息的 ”message” 事件和发送消息的 ”postMessage” 方法。比如http://damonare.cn
域的 A 页面通过 iframe 嵌入了一个http://google.com
域的 B 页面,可以通过以下方法实现 A 和 B 的通信
A 页面通过 postMessage 方法发送消息:
1 | window.onload = function () { |
postMessage 的使用方法
otherWindow.postMessage(message, targetOrigin);
- otherWindow: 指目标窗口,也就是给哪个 window 发消息,是 window.frames 属性的成员或者由 window.open 方法创建的窗口
- message: 是要发送的消息,类型为 String、Object (IE8 、 9 不支持 )
- targetOrigin: 是限定消息接收范围,不限制请使用 ‘*
B 页面通过 message 事件监听并接受消息 :
1 | var onmessage = function (event) { |
通过 window.name 跨域
在一个窗口生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每一个页面对 window.name 都有读写的权限,window.name 是持久的存在于一个窗口载入的所有页面,并不会因为新的页面的载入而被重置。
下面为 a.html 中代码
1 | window.name = '我是页面a中设置的值' |
b.html 中的代码
1 | <script>console.log(window.name);//读取window.name的值</script> |
服务器端设置 http header
这是需要在服务器端设置的,作为前端工程师我们不用详细掌握,但是要知道有这么个解决方案。而且,现在推崇的跨域解决方案是这一种,比 JSONP 简单许多。
1 | response.setHeader('Access-Control-Allow-Origin', 'http://m.juejin.com/') // 第二个参数填写允许跨域的域名称,不建议直接写 "*" |
XMLHttpRequest Level 2 使用指南
XMLHttpRequest 是一个浏览器接口,是的 Javascript 可以进行 HTTP(S)通信。
老版本的 XMLHttpRequest 对象
1 | //首先 ,新建一个XMLHttpRequest 的实例。 |
老版本的 XMLHttpRequest 的缺点
- 只支持文本数据的传送,无法用来读取和上传二进制文件。
- 传送和接收数据时,没有进度信息,只能提示有没有完成。
- 受到”同源限制”,只能向同一域名的服务器请求数据。
新版本的功能
- 可以设置 HTTP 请求发的时限。
- 可以使用 FormData 对象管理表单数据。
- 可以上传文件。
- 可以请求不同域名下的数据。(跨域请求)
- 可以获取服务器端的二进制数据。
- 可以获的数据传输的进度信息。