Skip to content
  • 什么是同源策略及限制
  • 前后端如何通信
  • 如何创建 Ajax
  • 跨域通信的几种方式

什么是同源策略及限制

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

  • cookie,localstorage 和 IndexDB 无法读取
  • DOM 无法获取
  • Ajax 请求不能发送

前后端如何通信

如何创建 Ajax(code

  • XMLHttpRequest 对象的工作流程
  • 兼容性处理
  • 事件的触发条件
  • 事件的触发顺序

跨域通信的几种方式(code

  1. 通过 JSONP 跨域
  2. CORS
  3. document.domain + iframe 跨域
  4. location.hash + iframe
  5. window.name + iframe 跨域
  6. postMessage
  7. nginx 代理跨域
  8. nodejs 中间件代理跨域
  9. WebSocket 协议跨域
  10. webpack proxy

jsonp

优缺点:

  1. JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
  2. 只能实现 get 一种请求、不安全 容易遭到 xss 攻击
javascript
<script src="http://www.abc.com/?data=name&callback=jsonp" charset="utf-8"></script>
<script type="text/javascript">
  // jsonp({
  //   data: {
  //
  //   },
  // });
</script>

CORS

CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。 普通跨域请求:只服务端设置 Access-Control-Allow-Origin 即可,前端无须设置,若要带 cookie 请求:前后端都需要设置。由于同源策略的限制,所读取的 cookie 为跨域请求接口所在域的 cookie,而非当前页 优缺点:

  1. 目前,所有浏览器都支持该功能(IE8+:IE8/9 需要使用 XDomainRequest 对象来支持 CORS)),CORS 也已经成为主流的跨域解决方案。
  2. 整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
  3. CORS 与 JSONP 的使用目的相同,但是比 JSONP 更强大。JSONP 只支持 GET 请求,CORS 支持所有类型的 HTTP 请求。JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。
javascript
// url(必选),options(可选)
fetch('/some/url/', {
  method: 'get',
})
  .then(function (response) {})
  .catch(function (err) {
    // 出错了,等价于 then 的第二个参数,但这样更好用更直观
  })

document.domain + iframe 跨域

此方案仅限主域相同,子域不同的跨域应用场景(网页一级域名相同,只是二级域名不同)。 实现原理:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。

html
<!-- 父窗口:(www.a.com/a.html) -->
<iframe id="iframe" src="http://child.a.com/b.html"></iframe>
<script>
  document.domain = 'a.com'
  let user = 'admin'
</script>

<!-- 子窗口:(child.a.com/b.html) -->
<script>
  document.domain = 'a.com'
  // 获取父窗口中变量
  alert('get js data from parent ---> ' + window.parent.user)
</script>

location.hash

利用 hash,场景是当前页面 A 通过 iframe 或 frame 嵌入了跨域的页面 B

javascript
// 在A中伪代码如下:
let Biframe = document.getElementsByTagName('iframe')
Biframe.src += '#data'

// 在B中的伪代码如下
window.onhashchange = function () {
  let data = window.location.hash
}

window.name + iframe 跨域

window.name 这个属性不是一个简单的全局属性 --- 只要在一个 window 下,无论 url 怎么变化,只要设置好了 window.name,那么后续就一直都不会改变,同理,在 iframe 中,即使 url 在变化,iframe 中的 window.name 也是一个固定的值

javascript
// 1.  a.html:(www.a.com/a.html)

const iframeNameProxy = (url, callback) => {
  let iframe = document.createElement('iframe')
  let state = 0

  // 加载跨域页面
  iframe.src = url

  // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
  iframe.onload = function () {
    if (state === 1) {
      // 第2次onload(同域c页)成功后,读取同域window.name中数据
      callback(iframe.contentWindow.name)
      destoryFrame()
    } else if (state === 0) {
      // 第1次onload(跨域页)成功后,切换到同域代理页面
      iframe.contentWindow.location = 'http://www.a.com/c.html'
      state = 1
    }
  }

  document.body.appendChild(iframe)

  // 获取数据以后销毁这个iframe,释放内存;同时也保证了安全(不被其他域frame js访问)
  function destoryFrame() {
    iframe.contentWindow.document.write('')
    iframe.contentWindow.close()
    document.body.removeChild(iframe)
  }
}

// 请求跨域b页面数据
iframeNameProxy('http://www.b.com/b.html', (data) => {
  alert(data)
})

// 2.  c.html:(www.a.com/c.html)
// src设置为当前域的c.html,中间代理页,与a.html同域,内容为空即可

// 3.  b.html:(www.b.com/b.html)
const person = {
  name: 'WuChenDi',
  age: 23,
}
window.name = JSON.stringify(person)

postMessage

窗口 A(http:A.com)向跨域的窗口 B(http:B.com)发送信息

javascript
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Bwindow.postMessage('data', 'http://B.com')
// 在窗口B中监听
Awindow.addEventListener(
  'message',
  function (event) {
    console.log(event.origin)
    console.log(event.source)
    console.log(event.data)
  },
  false
)

nginx 代理跨域

server {
    listen       80;
    server_name  localhost;

    # 匹配以/apis/开头的请求
    location ^~ /apis/ {
        proxy_pass http://www.xxx.com/; #反向代理
        proxy_cookie_domain http://www.xxx.com/;
    }

    location / {
        root   html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

WebSocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 使用 ws://(非加密)和wss://(加密)作为协议前缀。 该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

webpack的proxy是如何解决跨域的?

其实这里就像是反向代理一样。我们在使用webpack开发项目的时候,webpack的dev-server模块会启动一个服务器,这个服务器不止帮我们做了自动更新,同时也可以做到反向代理。就是我们把请求发送给webpack-dev-server, 然后webpack-dev-server再去请求后端服务器,服务之间的请求是没有跨域问题的,只要后端返回了webpack-dev-server就能拿到,然后再返回给前端。

链接参考: