什么是同源政策?

定义

  • 如果两个 URL 的协议、端口(如果有指定的话)和主机都相同的话,则这两个 URL 是同源的。
  • 当一个网页加载的资源(如脚本、样式表、图片等)来自于与当前页面不同的域名、协议或端口时,就称为跨域请求。

举例:

  • http://store.company.com/dir/page.htmlhttp://store.company.com/dir2/other.html 同源
  • http://store.company.com/dir/page.htmlhttp://news.company.com/dir/other.html不同源,因为主机不同

参考:


为什么要有同源政策?

同源政策限制的是非同源的请求,那非同源请求会带来什么问题呢? 假设今天有一个使用者登入某一银行网站www.bank.com ,同时他刚好在使用另一个不安全的网站例如www.stolemoney.com ,如果没有同源政策的话,这个stolemoney 网站可能可以轻易地存取这个使用者在www.bank.com 里的资料。

浏览器的同源政策就像是最基本的一层保护机制,让不同源的网站无法存取到资源和资料。另外要注意的是,这个阻挡机制是在最后浏览器收到服务器端回应后发生的; 也就是说,就算是非同源请求,如果服务器端没有做任何阻挡、并回传结果,浏览器端其实是会成功收到回应,但因为违反同源政策,浏览器会拦截这个回应、并报错。


跨域问题的解决

三类标签

  • 有类个标签允许跨域加载资源
    1. <img src=XXX>
    2. <link href=XXX>
    3. <script src=XXX>

代理

CORS

  • CORS 跨源资源共享(Cross-Origin Resource Sharing)。
  • 总体思路是:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许
  • 三种请求
    1. 简单请求
      • 需要同时满足以下两大条件
        1. 使用下列方法之一:GET HEAD POST
        2. Content-Type 的值仅限于下列三者之一:text/plain,multipart/form-data,application/x-www-form-urlencoded
    2. 需要预检的请求
    3. 附带身份凭证的请求
  • 服务器通过响应headerAccess-Control-Allow-Origin进行控制
  • 跨源资源共享 - MDN Web 文档术语表:Web 相关术语的定义 | MDN

postMessage

  • 语法: otherWindow.postMessage(message, targetOrigin, [transfer]);
  • 过程
    1. 获得对另一个窗口的引用(比如 targetWindow = window.opener)
    2. 在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息
    3. 接收消息的窗口可以根据需要自由处理此事件。
    4. 传递给 window.postMessage() 的参数(比如 message)将通过消息事件对象暴露给接收消息的窗口。
  • 安全问题
    • 如果你不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。
    • 如果你确实希望从其他网站接收 message,请始终使用 origin 和 source 属性验证发件人的身份。当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。
    • 当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。
  • 参考: window.postMessage - Web API | MDN

websocket

wip

JSONP

  • 服务端
app.get('/app/jsonp', (req, res) => {
  const responseData = { data: [{ id: 1, name: 'Brian' }, { id: 2, name: 'Peter' }] }
  const responseDataText = JSON.stringify(responseData)
  res.set('Content-Type', 'application/text')
  res.send(`jsonpCallback(${responseDataText})`)
})
  • 网页端
...
<script>
  function jsonpCallback(response) {
    console.log('jsonp response', response)
    // 任何你想對 data 執行的動作
  }
</script>
<script type="text/javascript" src="/app/jsonp"></script>
</body>

实际遇到过的问题

  • 描述: 一个页面里用到了三次同一个s3生成的signed url,一次是thumbnail,另一次是preview,第三次是在image的onload后使用fetch直接去获取s3的图片,我发现这里会使用缓存,显示是(memory cache)但这样的话它似乎就丢失了cors信息,就会报错blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
  • 代码如下
  • 解决方法: 将出现的两个<img>的crossOrigin都设为"anonymous"
  • 有效的原因
    1. 跨域图像加载的缓存行为:通过设置crossOrigin="anonymous",浏览器明确地按照跨域请求来处理这个图像,确保它从服务器获取时遵循 CORS 规范,并且缓存下来的资源携带正确的 CORS 信息。
    2. 避免重复跨域请求错误:由于图像是通过<img>标签加载且设置了 crossOrigin,接下来的 fetch 请求可以从缓存中获取具有正确 CORS 头的信息,避免了 CORS 错误。
  • 参考

参考: