资讯专栏INFORMATION COLUMN

前端跨域方法论

leejan97 / 3060人阅读

摘要:说明是否允许通讯同一域名允许同一域名下的不同文件夹允许不同端口号不允许不同协议不允许不同域名不允许主域相同,子域不同不允许跨域解决方案由于浏览器同源策略是允许标签这样的跨域资源嵌套的,所以标签的资源不受同源策略的限制。

前言

本着学习和总结的态度写的技术输出,文中有任何错误和问题,请大家指出。更多的技术输出可以查看我的 github博客。

整理了一些前端的学习资源,希望能够帮助到有需要的人,地址: 学习资源汇总。

跨域

跨域指的是协议(protocol ),域名(host),端口号(post)都不相同的资源之间尝试着进行交互通信,而由于受浏览器同源策略的限制,无法正常进行交互通信。

最常见的实际场景就是在项目开发过程中,会存在请求第三方其他域下的资源,例如:使用地图 API 的时候,设置密钥的时候需要设置白名单才能正常使用地图 API。

使用 AJAX 请求第三方不同域下的数据资源的时候,如果不处理跨域问题,便不能成功发送 HTTP 请求,且浏览器会发出错误警告。

同源策略

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

浏览器的同源策略的目的就是为了防止 XSS,CSRF 等恶意攻击。

同源策略的交互方式有三种:

通常允许跨域写操作,例如链接,重定向等。

通常允许跨域嵌套资源,例如 img,script 标签等。

通常不允许跨域读操作。

跨域场景

只有资源之间的协议,域名和端口号都相同,才是同一个源。

下面是关于同源以及不同源之间的跨域描述。

URL 说明 是否允许通讯
http://www.demo.com/a.html
http://www.demo.com/b.html
http://www.demo.com/c.html
同一域名 允许
http://www.demo.com/news/a.html
http://www.demo.com/center/b....
http://www.demo.com/server/c....
同一域名下的不同文件夹 允许
http://www.demo.com/a.html
http://www.demo.com:80/b.html
不同端口号 不允许
http://www.demo.com/a.html
https://www.demo.com/b.html
不同协议 不允许
http://www.demo.com/a.html
http://www.test.com/b.html
不同域名 不允许
http://www.demo.com/a.html
http://test.demo.com/b.html
主域相同,子域不同 不允许
跨域解决方案 1. JSONP

由于浏览器同源策略是允许 script 标签这样的跨域资源嵌套的,所以 script 标签的资源不受同源策略的限制。

JSONP 的解决方案就是通过 script 标签进行跨域请求。

前端设置好回调函数,并把回调函数当做请求 url 携带的参数。

后端接受到请求之后,返回回调函数名和需要的数据。

后端响应并返回数据后,返回的数据传入到回调函数中并执行。



也可以使用 AJAX GET 请求方式来跨域请求(axios GET 方式跨域同理)。


优缺点:

兼容性好,低版本的 IE 也支持这种方式。

只能支持 GET 方式的 HTTP 请求。

只支持前后端数据通信这样的 HTTP 请求,并不能解决不同域下的页面之间的数据交互通信问题。

2. CORS

CORS 跨域资源共享允许在服务端进行相关设置后,可以进行跨域通信。

服务端未设置 CORS 跨域字段,服务端会拒绝请求并提示错误警告。

服务端设置 Access-Control-Allow-Origin 字段,值可以是具体的域名或者 "*" 通配符,配置好后就可以允许跨域请求数据。

服务端如何设置跨域字段? 后端语言设置跨域的方式都不一致,具体可参考后端语言本身的 API。

Node 端设置

res.writeHead(200, {
    "Access-Control-Allow-Origin": "*"
});

// 或者使用了 Express 这样的框架
res.header("Access-Control-Allow-Origin", "*");

关于 CORS 的详细,可以参考这篇笔记,CORS跨域资源共享。

3. Server Proxy

通过服务端代理请求的方式也是解决浏览器跨域问题的方案。同源策略只是针对浏览器的安全策略,服务端并不受同源策略的限制,也就不存在跨域的问题。具体步骤如下:

前端正常请求服务端提供的接口。比如请求接口:http://localhost:3000 。

通过服务端设置代理发送请求,请求到数据后再将需要的数据返回给前端。比如设置的代理请求接口是 https://cnodejs.org/api/v1/to... ,服务端代理将数据请求回来之后再将数据 http://localhost:3000 接口返回给前端。

// 服务端代理请求代码
// 服务端只是简单的通过正常的 HTTP 请求的方式来代理请求接口数据
// 或者也可以使用 proxy 模块来代理,至于怎么使用 proxy 模块,待研究完善
var url = "https://cnodejs.org/api/v1/topics";        
https.get(url, (resp) => {
    let data = "";
    resp.on("data", chunk => {
        data += chunk;
    });
    resp.on("end", () => {
        res.writeHead(200, {
            "Access-Control-Allow-Origin": "*",
            "Content-Type": "application/json; charset=utf-8"
        });
        res.end(data);
    });
})
4. location.hash + iframe

location.hash + iframe 跨域通信的实现是这样的:

不同域的 a 页面与 b 页面进行通信,在 a 页面中通过 iframe 嵌入 b 页面,并给 iframe 的 src 添加一个 hash 值。

b 页面接收到了 hash 值后,确定 a 页面在尝试着与自己通信,然后通过修改 parent.location.hash 的值,将要通信的数据传递给 a 页面的 hash 值。

但由于在 IE 和 Chrmoe 下不允许子页面直接修改父页面的 hash 值,所以需要一个代理页面,通过与 a 页面同域的 c 页面来传递数据。

同样的在 b 页面中通过 iframe 嵌入 c 页面,将要传递的数据通过 iframe 的 src 链接的 hash 值传递给 c 页面,由于 a 页面与 c 页面同域,c 页面可以直接修改 a 页面的 hash 值或者调用 a 页面中的全局函数。

大致流程就是:

a 页面代码

b 页面代码

  

c 页面代码

优缺点:

hash 传递的数据容量有限。

数据直接暴露在 url 中。

5. document.domain + iframe

该方案只限于主域相同子域不同的资源跨域解决方案。

实际应用场景:

之前的项目开发中,经常碰到这样的跨域问题,大致类似于在开发新产品的产品页中,在没有正式上线之前,一般都是上传到内部的测试环境中,比如测试环境的域名是 test.admin.com/xxx/xxx,而项目的测试环境的域名是 consumer-test.admin.com/xxx/xxx 这样的,产品页是多带带分离部署上线,再通过 iframe 嵌套到项目中。在内部测试过程中,由于产品页测试环境和项目测试环境主域相同而子域不同,且产品页中需要用到项目中定义的全局公共资源,由于跨域问题,这些公共资源是获取不到的。

这种场景的跨域解决方案就是利用 document.domain 设置。在产品页和项目中将 document.domain 设置成相同域就可以实现跨域,嵌套的产品页就可以访问父页面的公共资源了。需要注意的一点就是,document.domain 的设置是有限制的,只能设置成自身或者更高级的父域,且主域必须相同。

项目页面


产品页

6. window.name + iframe

window.name 指的是当前浏览器窗口的名称,默认为空字符串,每个窗口的 window.name 都是独立的。iframe 嵌套的页面中也有属于自己的 window 对象,这个 window 是top window 的子窗口,也同样拥有 window.name 的属性。

window.name 的独特之处在于当在页面设置 window.name 的值,其实就是相当于给这个窗口设置了名称,而后在这个窗口加载其他页面(甚至不同域的页面),window.name 的值依然存在(如果没有重新设置那么值不会变化),并且 window.name 的值支持比较大的存储(2MB)。

例如: 随便找个页面打开控制台,给当前窗口设置名称。

window.name = "test-name";

设置好之后可以在这个窗口下跳转到其他页面

window.location = "https://www.baidu.com";

页面跳转到了百度首页,但是 window.name 的值依然是之前设置的值,因为是在一个窗口中跳转的页面,窗口名称并不会被修改。

具体的跨域解决方式如下。

http://localhost:8080/a.html 与 http://localhost:8081/b.html 跨域通信,a 页面通过 iframe 嵌套 b 页面,b 页面中设置好 window.name 的值,由于是不同域,a 页面不能直接访问到 b 页面设置的 window.name 的值,需要一个与 a 页面同域的中间页来代理作为 a 页面与 b 页面通信的桥梁。

a.html

b.html

中间代理页,只需要跟 a 页面保持同域就可以了,例如: http://localhost:8080/c.html 。

7. window.postMessage

postMessage 是 HTML5 的新特性,用于页面之间跨域通信。

postMessage 方法接受两个必要的参数:

message: 需要传递的数据。

targetOrigin: 数据传递的目标窗口域名,值可以是具体的域名或者 "*" 通配符。

a.html


b.html

总结

协议,域名,端口号不相同的资源之间相互通信,就会产生跨域问题。

处于安全考虑,浏览器的同源策略限制了不同域之间相互通信。

JSONP,CORS,Server Proxy 跨域解决方式的应用场景都是用于前后端之间的数据通信,其他跨域解决方案主要是解决窗口页面之间的数据通信。

JSONP 只支持 GET 方式的 HTTP 请求。

CORS 跨域资源请求需要后端支持。

Server Proxy 直接让后端代理发送请求。

后记

所有的跨域解决方案都有对应的 DEMO 实例,可在 DEMO 中查看。想要看运行效果,可以全局安装 http-server 模块。

npm install -g http-server

本着学习和总结的态度写的技术输出,文中有任何错误和问题,请大家指出。更多的技术输出可以查看我的 github博客。

整理了一些前端的学习资源,希望能够帮助到有需要的人,地址: 学习资源汇总。

参考

https://zhuanlan.zhihu.com/p/...

https://segmentfault.com/a/11...

https://segmentfault.com/a/11...

https://juejin.im/post/5815f4...

https://juejin.im/post/59c132...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98076.html

相关文章

  • 彻底弄懂跨域问题

    摘要:用于告知浏览器可以将预先检查请求返回结果缓存的时间,在缓存有效期内,浏览器会使用缓存的预先检查结果判断是否发送跨域请求。 跨域,老生常谈的问题 简述 作为一只前端菜鸟,跨域方面只懂得JSONP和CORS,并未曾深入了解。但随着春招越来越近,就算是菜鸟也要猛振翅膀。近几日仔细研究了跨域问题,写下这篇文章,希望对开发者们有所帮助。在读本文前,希望您对以下知识略有了解。 浏览器同源策略 n...

    rose 评论0 收藏0
  • 彻底弄懂跨域问题

    摘要:浏览器同源策略我们为何要研究跨域问题因为浏览器的同源策略规定某域下的客户端在没明确授权的情况下,不能读写另一个域的资源。 跨域,老生常谈的问题 简述 作为一只前端菜鸟,跨域方面只懂得JSONP和CORS,并未曾深入了解。但随着春招越来越近,就算是菜鸟也要猛振翅膀。近几日仔细研究了跨域问题,写下这篇文章,希望对开发者们有所帮助。在读本文前,希望您对以下知识略有了解。 浏览器同源策略 n...

    CoorChice 评论0 收藏0
  • 前端培训-中级阶段(11、12)- 跨域请求原理以及实现(2019-08-22期)

    摘要:上节我们讲了同源策略,这节我们讲讲如何跨域。当这些从的脚本执行出错,因为违背了同源策略为了保证用户信息不被泄露,错误信息不会显示出来,取而代之只会返回一个。 前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每...

    binaryTree 评论0 收藏0
  • ajax跨域,这应该是最全的解决方案了

    摘要:关于,强烈推荐阅读跨域资源共享详解阮一峰另外,这里也整理了一个实现原理图简化版如何判断是否是简单请求浏览器将请求分成两类简单请求和非简单请求。 前言 从刚接触前端开发起,跨域这个词就一直以很高的频率在身边重复出现,一直到现在,已经调试过N个跨域相关的问题了,16年时也整理过一篇相关文章,但是感觉还是差了点什么,于是现在重新梳理了一下。 个人见识有限,如有差错,请多多见谅,欢迎提出iss...

    ytwman 评论0 收藏0
  • 20K前端大佬面试(关于如何回答ajax跨域问题)

    摘要:在接触前端开发起,跨域这个词就一直以很高的频率在我们学习工作中重复出现,最近在工作中遇到了跨域的相关问题,这里我把它总结记录一下。 在接触前端开发起,跨域这个词就一直以很高的频率在我们学习工作中重复出现,最近在工作中遇到了跨域的相关问题,这里我把它总结记录一下。关于跨域,有N种类型,现在我只专注于ajax请求跨域(ajax跨域只是属于浏览器同源策略中的一部分,其它的这里不做介绍),内容...

    Yangyang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<