跨域这两个字就像一块狗皮膏药一样黏在每一个前端开发者身上,无论你在工作上或者面试中无可避免会遇到这个问题。如果在网上搜索跨域问题,会出现许许多多方案,这些方案有好有坏,但是对于阐述跨域的原理和在什么情况下需要用什么方案,缺少系统性的说明。大家在工作中可能因为大佬们已经配置好了,不会产生跨域,但是作为一个前端的开发人员,面对跨域的问题,还是需要从原理上去理解跨域的原因,在不同的情况中,我们该如何去处理。
WMS6.0 是一款专门为仓储业务打造的合作开发平台,前台 BP 可以独立开发或者定制现有的流程,接入到 WMS6.0 中,实现自定义业务,使前台 BP 只需要关注自己的业务,不用专注其他功能,提升前台 BP 的开发效率。。
作为一个合作平台,WMS6.0 PC 端支持独立页面扩展和页面内部功能扩展,支持前台 BP 可以进行独立部署,实现最大程度的解耦。接入方案如下:
在 bp 接入平台的过程中,我们遇到了各种各样的问题,如前后端如何联调、如何在不冲突的情况下自定义全局属性、如何部署上线等等,下面我们主要就前后端联调中遇到的跨域问题进行讨论。
在使用上述预留插槽的接入方式时,为了通用模块与接入模块之间的数据同步等方便进行,WMS6.0 中并没有使用老式的 iframe,而是采用了 vue 注册的方式,实现在同一个页面中加载。因此合作方在独立模块中发起的服务端请求,其来源其实仍是当前通用页面。
而 WMS6.0 并不能确保所有的合作方服务端均在同一个域名下,由此也就产生了各种交互问题。
我们先来看一下 WMS6.0 现有的通用网络请求整体链路。
当用户触发了网络请求,会通过基站或者仓库的路由发出,然后通过网络到达物流网关,物流网关把请求转发到 Nginx,Nginx 会把请求分发到具体的服务器上进行数据处理。
下面我们就抽取一个 WMS6.0 通过物流网关访问的请求,作为实例来看一下。
通过 response Headers(相应头)我们可以看到,公司现有的物流网关会对指定域名的页面进行 CORS 跨域处理。通过 Access-Control-Allow-Origin: http://a..com,我们可以知道物流网关可以接受来自指定域 http://a..com 的跨域资源请求,不会产生跨域报错。
但是咱们部分 bp 合作方的接口并不是通过物流网关的,这就需要我们自己对此类接口进行跨域处理了。假如没有进行跨域处理,那么就会报下面的错了。
报错解析:
从源 “本地路径” 访问 “目标路径 (请求链接)” 的文本传输请求已被 CORS 策略阻塞:对预检请求的响应未通过访问控制检查。请求的资源上不存在'Access- control - allow - origin '报头。
错误原因:
本地路径和目标路径不在同一个域名下引起的跨域问题。
同时需要注意的是,就算两个域名是同一个二级域名、不同三级域名的时候,例如 a.baidu.com 和 b.baidu.com ,也是属于不同域的,仍会出现这个问题。
那么到底什么是跨域,跨域既然影响了我们的开发工作,那又为什么要有对跨域的限制呢?下面让我们来了解一下跨域的历史产生原因和作用吧。
以下内容为个人猜测,仅供参考,勿喷 🤞
互联网始于 1969 年的美国。在互联网的最早期,美军在 ARPA(阿帕网,美国 guofang 部研究计划署)制定的协定下,首先用于军事连接。
随后主要都是美国高校连入的网络,如美国西南部的加利福尼亚大学洛杉矶分校、斯坦福大学研究学院、UCSB(加利福尼亚大学)和犹他州大学的四台主要的计算机。服务器上存放的都是公开资料。
这个时候网站更像是一个公共图书馆,账户密码都没有,更没存放着什么机密资料
后来,有人觉得可以在上面放一些私人资料,私人信息。于是为了安全,便有了账户和密码。可是如果每次访问都需要输入账户和密码,是一件很烦的事情。
所以浏览器实现了 cookie,用来存储用户登陆的账户和密码。当用户访问了曾经已经登陆过的网站,浏览器将会自动在请求中加入账户和密码,而账户和密码通常是通过 Request Header(请求头)中的 cookie 或指定的头信息进行通信的。
而直接存储账户和密码太过于危险,如果被攻破,损失相当大。所以浏览器都不直接存储账户和密码,而是存储登陆令牌。
但是存储登陆令牌也有一个问题,如果你登陆了某个流氓网站,同时这个流氓网站在它的 JS 里访问了你已经登录的其他网站,那么就能够拿到你已经登录的其他网站里面的一些重要数据。
所以浏览器为了安全是不能够让这个流氓网站访问你已经登录的其他网站的。由此产生了浏览器的同源策略:哪里来的,就只能访问哪里的数据。
综上,我们就可以基本了解对跨域的定义了,如下:
跨域是指向一个与当前页面所在域不同的目标地址发送请求的过程,这样之所以会产生跨域报错是因为浏览器的同源策略限制。看起来同源策略影响了我们开发的顺畅性。实则不然,同源策略存在的必要性之一是为了隔离攻击。
MDN 上对同源策略的解释为:
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
下面就拿同源策略隔离的主要攻击之一 CSRF 为例讲述下同源策略存在的必要性:
CSRF,cross-site request forgery,又称跨站请求伪造,指非法网站挟持用户 cookie 在已登陆网站上实施非法操作的攻击,这是基于部分页面使用 cookie 在网站免登和用户信息留存。
正常网站免登的请求流程如下:
受攻击场景:
bank.com 网站是一家银行,在用户登录以后,bank.com 网站在用户的当前终端上设置了一个 Cookie,这其中包含了一些隐私信息 (比如存款金额)。
如果这个时候,七大姑在社交 app 上给你发了一篇养生文章链接,其实这个网页是个 diaoyu 网站 evil.com,访问链接后就把你重定向到一个嵌入了 iframe 的攻击网站。
而这个时候如果没有跨域限制,这个 iframe 会自动加载银行网站的留存信息,读取到 bank.com 网站的 Cookie,那么用户的信息就会泄露,更可怕的是,Cookie 往往是用来保存用户的登录状态,如果用户没有退出登录,其他的网站就可以冒充用户,为所欲为,控制 iframe 的 DOM,通过一系列骚操作把你卡里的钱转走。
没有同源策略:
有同源策略:
而同源策略,也就是跨域限制的出现,限制了 cookie 的命名区域,使攻击者无法直接获取 cookie 的内容本身。
下面就让我们一起来了解一下什么是同源策略。
在了解同源策略之前,我们需要先对一个 url 的各个组成部分进行初步了解:
以上,我们已经大致了解了一个 url 的基本组成。
它是由 Netscape(美国网景公司)提出的一个重要的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。
同源策略作为浏览器安全的基石,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,如个人信息将不再具有安全性。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
它的核心就在于它认为自任何站点加载的内容都是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。
因此,出于安全原因,对于跨源 HTTP 请求,浏览器禁止发起请求,或者允许发起请求,服务端也能收到请求并正常返回结果,但是浏览器会对返回结果进行拦截。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略,这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非服务器同意访问。譬如服务器对预检请求的响应 Header 中有 Access-Control-Allow-Origin: *,那么跨域请求即可正确访问。
简单来说,同源策略就是浏览器的一个安全限制,它阻止了不同【域】之间进行的数据交互。
那么是如何定义一个请求是否满足同源要求的呢?
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.jshttp://www.a.com/b.js | 同一域名,不同路径 | 允许 |
http://www.a.com:8080/a.jshttp://www.a.com/a.js | 同一域名,不同端口 | 不允许 |
http://www.a.com/a.jshttps://www.a.com/a.js | 同一域名,不同协议 | 不允许 |
http://www.a.comhttp://www.b.com | 域名不同 | 不允许 |
http://www.a.com/a.jshttp://script.a.com/a.js | 主域相同,子域不同 | 不允许 |
另外,我们知道通过 JavaScript 脚本可以拿到其他窗口的 window 对象。如果是非同源的网页,目前允许一个窗口可以接触其他网页的 window 对象的九个属性和四个方法。
ps: 超链接标签、
标签中的 action 行为也可以进行跨域综上,我们完成了对跨域的初识,后面我们将对跨域的解决方案进行探讨,从上述的九种跨域解决方案进行一一描述,敬请期待。
作者:京东物流 李菲菲
来源:京东云开发者社区 自猿其说 Tech 转载请注明来源