12.27 十大常见的「前端跨域」解决方案

点击上方“Web前端进阶指南”关注我呦

跟程序员小强一起学前端

十大常见的「前端跨域」解决方案

跨域在我们前端也是最常见的问题之一,作为一名前端开发人员,相信大家都知道跨域是因为浏览器的同源策略所导致的。

但是仅仅知道是没有用的,公司里很多同事都知道是因为浏览器的同源策略引起的,却不知根源,更不知如何去解决,他们经常在网站中引用iframe,虽说没有出现任何复杂性的问题,却也不去解决,后端给个接口,说跨域了,然后让他们去解决,不会啊,三番五次的问,而且这问题一阻碍就耗费了很多的时间,那么到底跨域是怎么产生的,作为我们前端人员必须去了解,去解决。

其实想知道产生这个问题的“大哥”,我们必须去了解浏览器的同源策略。

同源策略

同源策略是浏览器最核心也是最基本的安全功能,所有支持JavaScript的浏览器都会使用这个策略,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,可以说,我们的Web是构建在同源策略基础之上的,反之,我们的浏览器也是针对同源策略的一种实现。

例如,我们在浏览器上打开两个页面,一个是头条,一个是悟空问答,当浏览器的头条页面去执行一个脚本文件的时候会检查这个脚本是属于那个页面的,就是所谓的检查是否同源,只有和头条同源的脚本才会被执行,如果不是同源,那么在请求数据时,浏览器就会报错,提示访问拒绝。

再说一个简单例子,当我们在写页面的时候,想在页面中调用别的网站的东西,比如图表,图片,内容文本的时候,发现放在当前页面的时候,有点不适合,想改一下,于是你就用js获取iframe里的文档元素,想去修改它,这时候就会发现浏览器报错。

想想如果没有同源策略,我可以在不同域的iframe之间直接访问,我可以直接把银行的网站用iframe嵌套到我得网站里,页面调整到和银行页面一样,你输入域名进来,看见的是银行网站页面,这时候你输入账号密码进行操作,我的主网站就可以跨域访问到银行页面的DOM节点,于是乎就拿到了你的账号密码,可怕不。

同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接受。

说到这,非同源就是在请求数据时,浏览器会拒绝请求,不让你用别的地方的东西。

所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

来个更直白的,我们用一张图给出下面URL同源检测的示例:

URL:http://pc.abc.com/index.html

十大常见的「前端跨域」解决方案

同源检测示例

这样就明白了浏览器同源策略的原理了吧[嘿嘿]

跨域

看完上面有的小伙伴就问,那不用不就行了吧,我不去别的访问请求,我就在同一个服务器访问请求不就行了,哎,你还年轻啊,不知道别人家的媳妇好啊[哈哈][哈哈],开玩笑的啊。还是自家媳妇好!

跨域的优势能充分地利用分布式集群系统,是某些服务压力分散到多台服务器上,就像我们一个团队做一个项目,分配下去,最后整合,这样效率就很高,但质量就得不到保证,同样的我们把某些服务分散到多台服务器,压力是小了,但安全性就得不到保证。

不跨域的前提是前台页面和后台服务器都在一个服务器上,前提是应用得小,服务器压力不会很大,好处就是安全性高,处理简单。

目前计算机行业正在向高集成、多并发、低耦合的方向发展,所有的基础服务都会以接口的方式提供,就像百度地图、微信、小程序、支付宝等,这样一来,就会牵扯到跨域,我们就必须去解决,处理好跨域和安全问题是猿们不许去做的事。

从上面我们知道是因为浏览器的同源策略引起了跨域,也正是有了跨域限制,我们才能安全上网,那有时候我们必须去做这件事,就是去请求别的资源,那怎么解决吗?于是乎就有了我这样的“好心人”写下了解决方法。

我来说说我解决过的跨域问题,没有解决过的我给你附上链接地址,大家可以去看看,张大神的博客也有。

跨域解决方案

1、 通过jsonp跨域

Jsonp是Json的一种“使用模式”,他就可以解决浏览器遇到的跨域问题,我们可以动态创建script,再请求一个带参网址实现跨域通信。用Jsonp请求得到的是JavaScript,相当于直接用JavaScript解析。

但是,只能实现get一种请求。

1.1、原生js实现:

十大常见的「前端跨域」解决方案

服务器端返回:

<code>handleCallback({"status": true, "user": "admin"})/<code>

1.2、AJAX实现:

十大常见的「前端跨域」解决方案

1.3、vue.js实现

十大常见的「前端跨域」解决方案

2、document.domain + iframe跨域

这个适合在主域名相同,子域名不同的情况下使用,这时候用它就显得很轻松了,我们可以在两个页面都通过js强制设置document.domain为基础主域,这样就实现了同域。

2.1、父窗口(http://www.test.com/a.html)

十大常见的「前端跨域」解决方案

2.2、子窗口(http://child.com/b.html)

十大常见的「前端跨域」解决方案

3、location.hash + iframe跨域

通过location.hash + iframe来解决跨域,就是说a页面想和b页面实现通信,可以通过c页面来实现,三个页面之前可以利用 iframe 的location.hash传值,相同域之间直接 js 访问来通信。

假如a和b不同域,b和c不同域,c和a同域,那么,c就可以通过

parent.parent 来访问a页面中所有的对象。

3.1、a.html:(http://www.test1.com/a.html)

十大常见的「前端跨域」解决方案

3.2、b.html:(http://www.test2.com/b.html)

十大常见的「前端跨域」解决方案

3.3、c.html:(http://www.test1.com/c.html)

十大常见的「前端跨域」解决方案

4、 window.name + iframe跨域

在 iframe 中,window.name是一个固定的值,我们可以利用它实现跨域。只要你不关闭页面,来回之间跳转改变URL,window.name是不会改变的,而且每一个打开的页面都能获取并修改window.name的值。

4.1、a.html:(http://www.test1.com/a.html)

十大常见的「前端跨域」解决方案

4.2、fuzhu.html:(http://www.test1.com/fuzhu.html)这是个辅助页面,啥也不用管。但是与a.html同域。

4.3、b.html:(http://www.test2.com/b.html)

<code>/<code>

通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

5、postMessage跨域

在h5中新增了postMessage方法,postMessage可以实现跨文档消息传输,我们可以通过Windows的message事件来监听发送跨文档消息传输内容。

主页面 A 通过 iframe 加载跨域页面 B,页面 B 加载完毕后需要获取主页面 A 保存在 localStorage 内的 jwt 令牌。

5.1、a.html :(http:/ /test1.com/a.html)

十大常见的「前端跨域」解决方案

5.2、b.html (http:/ /test2.com/b.html)

十大常见的「前端跨域」解决方案

6、跨域资源共享(CORS)

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

浏览器将CORS请求分成两类:简单请求

(simple request)和非简单请求(not-so-simple request)。

注意:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。

目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

6.1、原生ajax实现

十大常见的「前端跨域」解决方案

6.2、jQuery ajax实现

十大常见的「前端跨域」解决方案

6.3、Vue框架

a) axios设置

<code>axios.defaults.withCredentials = true/<code>

b)vue-resource设置

<code>Vue.http.options.credentials = true/<code>

7、nginx代理跨域

个人觉得使用 nginx 是一种较为简单直接彻底的办法。

7.1、 nginx配置解决iconfont跨域

浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。

<code>location / {
add_header Access-Control-Allow-Origin *;
}/<code>

7.2、 nginx反向代理接口跨域

跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

nginx具体配置:

十大常见的「前端跨域」解决方案

前端代码示例:

<code>var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();/<code>

8、Nodejs中间件代理跨域

通过Nodejs来进行代理接口。不过要实现这个前提是,前端开发环境必须运行在Nodejs服务中,所幸的现在前端的开发自动化工具都是建立在nodejs上的,所以也没必要顾虑这么多。

8.1、 非vue框架的跨域(2次跨域)

利用node + express + http-proxy-middleware搭建一个proxy服务器。

前端代码示例:

<code>var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问http-proxy-middleware代理服务器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();/<code>

中间件服务器示例:

十大常见的「前端跨域」解决方案

8.2、vue框架的跨域(1次跨域)

利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。

webpack.config.js部分配置:

十大常见的「前端跨域」解决方案

9、WebSocket协议跨域

原理:原生WebSocket API使用起来不太方便,利用webSocket的API,可以直接new一个socket实例,然后通过open方法内send要传输到后台的值,也可以利用message方法接收后台传来的数据。后台是通过

<code>new WebSocket.Server({port:3000})/<code>

实例,利用message接收数据,利用send向客户端发送数据。

9.1、前端代码示例:

十大常见的「前端跨域」解决方案

Nodejs socket后台代码示例:

十大常见的「前端跨域」解决方案

10、Flash跨域

年龄久远的程序员们用过,我也只是见过,没用过,我也来整理一下。

原理很简单,无须对Flash做任何处理,只要在目标域 b.domain.com 的根目录上放入一个策略文件crossdomain.xml即可。

Flash访问另一个域的数据,flash player 会自动从目标域根域加载策略文件 crossdomain.xml。

如果访问的数据所在的域在策略文件中,则数据将可访问。

10.1、建立crossdomain.xml

<code>  

<cross-domain-policy>
<allow-access-from>
<allow-access-from>
<allow-access-from>
<allow-access-from>
/<cross-domain-policy>/<code>

10.2、把文件 crossdomain.xml 放入被Flash访问的域(b.domain.com)根目录上即可。

总结

目前CORS已经成为主流的跨域解决方案,但由于其不安全性,个人觉得使用 nginx 是一种较为简单直接彻底的办法。具体看项目实战需求吧。


分享到:


相關文章: