聊一聊我对跨域的理解和最佳实践

跨域是前端开发中一个非常常见的问题,尤其是随着单页应用(Single Page Application, SPA)的兴起,前后端分离开发和部署,前端在本地开发和部署的过程中都会面临着跨域问题。我们再次聊聊跨域这个话题,以及项目中对跨域的一些实践经验,希望带来一些新的收获。

什么是跨域

首先我们需要了解下什么是同源策略,MDN 中是这样介绍的:

同源策略是浏览器的一个重要安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行操作。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

从这段话我们得知,同源策略是浏览器安全策略,(Origin)是判断是否满足同源策略的条件。我们常说的跨域,准确来说应该是跨源,目的是不受同源策略的限制去操作另一个源的资源。例如当我们在本地开发时,源是 http://localhost:3000,而服务端接口的地址是 https://api.feishu.cn,此时通过 Fetch API 访问接口就会产生跨域。

同源策略源自于浏览器,反之在非浏览器的环境下,一般是没有同源策略限制的。例如在 Node.js 中,我们可以请求任意的网址并得到结果。因此基于 Node.js 的服务端可以直接跨域访问资源。

当然我们也可以关闭浏览器的同源策略,关闭后浏览器环境下也可以直接跨域。以 Chrome 为例,通过给 Chrome 增加启动参数:

$ /path/to/chrome.app --disable-web-security

即可关闭。关闭后浏览器会有安全风险,建议只用在本地开发上

源由协议域名(准确来说是主机名,因为除了域名也可以是 IP)、端口号共同决定,三者完全相同的两个 URL 会被认为是同源。举几个例子:

  • https://www.bytedance.comhttps://jobs.bytedance.com,域名不同,不同源
  • http://www.bytedance.comhttps://www.bytedance.com,协议不同,不同源
  • http://localhosthttp://localhost:8080,端口不同(80 和 8080),不同源

特别的,当我们直接打开一个 HTML 文件时,使用的是 file 协议,请求 HTTP 资源时协议不同,会产生跨域。

源是允许被有限度的修改,浏览器提供了 API 可以将子域名下的源修改为父域名的源,例如我们在 https://open.feishu.cn 下执行:

document.domain = 'feishu.cn';

此时页面的源就变成了 https://feishu.cn 满足同源策略,我们就可以直接访问父域名下的资源。如果修改为非父域名(如 bytedance.com),浏览器会报错。

同源策略控制不同源之间的操作,这些操作通常分为三类:

  • 资源嵌入:一般是被允许的。例如

    总结

    我们从三个角度讨论了跨域是什么、为什么需要同源策略以及跨域的解决方案和实践,重点总结如下:

    • 同源策略是浏览器的安全策略,Node.js 环境没有跨域限制
    • 浏览器的同源策略通过启动参数 disable-web-security 可以关闭
    • 通过协议、域名、端口号三元组判断同源
    • 通过 document.domain 可以有限度的修改源
    • 简单请求与非简单请求的区别,非简单请求会先发起预检请求
    • 同源策略保证了浏览器的安全性
    • 跨域通过增加响应头 Access-Control-Allow-Origin 解决,也可以借助代理实现
    • 跨域的一些最佳实践:

      • 跨域请求通过 Access-Control-Allow-Credentials 携带 Cookie
      • 通过请求头 Origin 允许白名单内的源跨域请求
      • 通过 Access-Control-Expose-Headers 获取跨域请求返回的某些响应头
      • 获取资源尽量使用简单请求,以减少请求耗时
      • CDN 资源也需要允许跨域

    如果有其它关于跨域的最佳实践,欢迎分享

你可能感兴趣的:(前端面试http)