同源策略的概念和具体限制

限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。

源:包括三个部分:协议、域名、端口(http协议的默认端口是80),如果有任何一个部分不同,则源就不同,那就是跨域了。

限制:这个源的文档没有权利去操作另一个源的文档。

  1. cookie、localStorage、indexedDB无法获取
  2. 无法获取和操作dom
  3. 不能发送Ajax请求。我们要注意,Ajax只适合同源的通信。


前后端如何通信

  1. Ajax:不支持跨域
  2. WebSocket:不受同源策略的限制,支持跨域
  3. CORS:不受同源策略的限制,支持跨域。一种新的通信协议标准。可以理解成是:同时支持同源和跨域的Ajax


如何创建AJAX

在回答 Ajax 的问题时,要回答以下几个方面:

  • XMLHttpRequest 的工作原理
  • 兼容性处理 XMLHttpRequest只有在高级浏览器中才支持。
  • 事件的触发条件
  • 事件的触发顺序

XMLHttpRequest有很多触发事件,每个事件是怎么触发的。

XMLHttpRequest的工作原理

  1. 创建XMLHttpRequest 对象。
  2. 使用open方法设置请求的参数。open(method, url, 是否异步)。
  3. 发送请求。
  4. 注册事件。 注册onreadystatechange事件,状态改变时就会调用。

如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。

  1. 获取返回的数据。

发送 get 请求和 post 请求

  1. var ajax = function(params) {
  2. // 创建对象
  3. var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
  4. xhr.open(params.type || "get", params.url + getParams()) // 设置请求参数
  5. // get请求
  6. xhr.send() // 发送请求
  7. if(params.type === 'post') {
  8. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  9. }
  10. // post请求
  11. // 将数据通过send方法传递
  12. xhr.send(getParams());
  13. xhr.onreadystatechange = function () { // 注册事件
  14. if (xhr.readyState == 4 && xhr.status == 200) {
  15. params.callback && params.callback(xhr.response)
  16. }
  17. }
  18. }
  19. function getParams() { // 拼接参数字符串
  20. return ''
  21. }

事件的触发条件

20180307_1443.png

事件的触发顺序

20180307_1445.png

跨域通信的几种方式

  1. JSONP
  2. WebSocket
  3. CORS
  4. Hash
  5. postMessage

JSONP

原理:通过<script>标签的异步加载来实现的。只支持get请求

实现:动态创建script标签插入到html,后台接受后执行callback

Websocket

  1. //创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。
  2. var ws = new WebSocket('wss://echo.websocket.org')
  3. //把请求发出去
  4. ws.onopen = function (evt) {
  5. console.log('Connection open ...')
  6. ws.send('Hello WebSockets!')
  7. }
  8. //对方发消息过来时,我接收
  9. ws.onmessage = function (evt) {
  10. console.log('Received Message: ', evt.data)
  11. ws.close()
  12. }
  13. //关闭连接
  14. ws.onclose = function (evt) {
  15. console.log('Connection closed.')
  16. }

CORS

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定 Access-Control-Allow-Credentials 字段。另一方面需要前端设置

  1. xhr.withCredentials = true

Hash

url的#后面的内容就叫Hash。Hash的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。
使用场景Iframe跨域 A页面内通过Iframe嵌入了跨域的B页面

  1. // A页面代码
  2. var B = document.getElementsByTagName('iframe');
  3. //我们可以把JS对象,通过 JSON.stringify()方法转成 json字符串,发给 B
  4. B.src = B.src + '#' + 'jsonString';
  5. // B页面代码
  6. //通过onhashchange方法监听,url中的 hash 是否发生变化
  7. window.onhashchange = function () {
  8. var data = window.location.hash;
  9. };

postMessage

简介

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递
postMessage(data,origin)方法接受两个参数:

  1. data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果,
  2. origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为”*”,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/“.

使用

子页面向父页面传递消息

  1. <!-- index.html -->
  2. <iframe src="http://127.0.0.1:5500/frame.html" frameborder="1"></iframe>
  3. <script>
  4. window.addEventListener('message', function(e){
  5. console.log(e.data)
  6. },false)
  7. </script>
<!-- frame.html -->
<h1>frame page</h1>
<script>
  window.top.postMessage('message from iframe1');
</script>

父页面向子页面传递消息

<!-- index.html -->
<iframe src="http://127.0.0.1:5500/frame.html" frameborder="1"></iframe>
<script>
  window.onload = function () {
    var frame = window.frames[0];
    frame.postMessage('message from parentwindow', '*');
  }
</script>
<!-- frame.html -->
<h1>frame page</h1>
<script>
    window.addEventListener('message',function(e){
      console.log(e.data)
    },false)
</script>