跨域的 N 种实现方式
同源策略引起的跨域问题
同源策略是浏览器的行为,是为了保护本地数据不被 JavaScript 代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。需要注意的是,很多人以为同源策略是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是,请求正常发出,后端接口正常响应,只不过数据到了浏览器后被丢弃了。

同源跨域通信
JSONP
CORS
postMessage
WebSocket
nginx 反向代理
CORS 是为了解决 AJAX 跨域的问题
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 CORS 背后的基本思想就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
服务器端对于 CORS 的支持,主要就是通过设置 Access-Control-Allow-Origin 来进行的。如果浏览器检测到相应的设置,就可以允许 Ajax 进行跨域的访问。
比如一个简单的使用 GET 或者 POST 发送的请求,它没有自定义头部,而主体内容是 text/plain。在发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。下面是 Origin 头部的一个示例。
Origin: http://www.nczonline.net 如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发'*')。例如:
Access-Control-Allow-Origin: http://www.nczonline.net 如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求进而响应都不包含 cookie 信息。
JSONP 是通过动态元素来使用的,使用时可以为 src 属性指定一个跨域 URL。 这里的<scriot>元素与<img>元素类似,都有能力不受限制的从其他域加载资源。因为 JSONP 是有效的 JS 代码,所以在请求完成后,即在 JSONP 响应加载到页面中以后,就会立即执行。来看一个例子。
CORS 和 JSONP 对比
JSONP 只能实现 GET 请求,而 CORS 支持所有类型的 HTTP 请求。 使用 CORS,开发者可以使用普通的 XMLHttpRequest 发起请求和获得数据,比起 JSONP 有更好的错误处理。 JSONP 主要被老的浏览器支持,它们往往不支持 CORS,而绝大多数现代浏览器都已经支持了 CORS
跨域的 N 种实现方式
解决跨域的几种方案
跨域, 什么是跨域?跨域有什么好处?跨域有什么不好?怎么实现跨域?
什么是跨域
只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作. 对于端口和协议的不同,只能通过后台来解决。
跨域有什么好处
防止 CSRF 攻击、同源策略:隔离潜在恶意文件的重要安全机制
或者使用 CSP 建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。 两种方式开启 CSP。
设置 HTTP Header 中的 Content-Security-Policy
设置 meta 标签的方式 meta http-equiv="Content-Security-Policy"
Content-Security-Policy: default-src 'self'
Content-Security-Policy: img-src https://*
Content-Security-Policy: child-src 'none'
同源策略限制以下几种行为
同源策略限制内容有:
Cookie、LocalStorage、IndexedDB 等存储性内容
DOM 节点
AJAX 跨域请求的数据
怎么实现跨域访问
JSONP
JSONP 跨域 GET 请求是一个常用的解决方案,下面我们来看一下 JSONP 跨域是如何实现的,并且探讨下 JSONP 跨域的原理
利用在页面中创建
节点的方法向不同域提交 HTTP 请求的方法称为 JSONP,这项技术可以解决跨域提交 Ajax 请求的问题。JSONP 的工作原理如下所述
假设在 http://example1.com/index.php
这个页面中向 http://example2.com/getinfo.php 提交 GET 请求,我们可以将下面的 JavaScript 代码
放在 http://example1.com/index.php 这个页面中来实现:
代码如下:
当 GET 请求从 http://xx1.html 返回时,可以返回一段 JavaScript 代码,这段代码会自动执行,可以用来负责调用 http://xxx2.html 页面中的一个 callback 函数
JSONP 的优点是:它不像 XMLHttpRequest 对象实现的 Ajax 请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要 XMLHttpRequest 或 ActiveX 的支持;并且在请求完毕后可以通过调用 callback 的方式回传结果。
JSONP 的缺点则是:它只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题。
再来一个例子:
这种方式其实是上例$.ajax({..}) api的一种高级封装,有些$.ajax api 底层的参数就被封装而不可见了。
jquery $.ajax方法名有误导人之嫌。 如果设为dataType: 'jsonp',这个$.ajax 方法就和 ajax
XmlHttpRequest 没什么关系了,这种跨域方式其实与 ajax XmlHttpRequest 协议无关了。 取而代之的则是 JSONP 协议。JSONP 是一个非官方的协议,它允许在服务器端集成 Script tags 返回至客户端,通过 javascript callback 的形式实现跨域访问。
Jsonp 的执行过程如下: 首先在客户端注册一个 callback (如:'jsoncallback'), 然后把 callback 的名字(如:jsonp1236827957501)传给服务器。注意:服务端得到 callback 的数值后,要用 jsonp1236827957501(......)把将要输出的 json 内容包括起来,此时,服务器生成 json 数据才能被客户端正确接收。
然后以 javascript 语法的方式,生成一个 function, function 名字就是传递上来的参数 'jsoncallback'的值 jsonp1236827957501 . 最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析 script 标签,并执行返回的 javascript 文档,此时 javascript 文档数据,作为参数, 传入到了客户端预先定义好的 callback 函数(如上例中 jquery $.ajax()方法封装的的 success: function (json))里。
可以说 jsonp 的方式原理上和</>是一致的(qq 空间就是大量采用这种方式来实现跨域数据交换的)。JSONP 是一种脚本注入(Script Injection)行为,所以有一定的安全隐患。
那 jquery 为什么不支持 post 方式跨域呢?
跨域资源共享 CORS 什么是 CORS
CORS(cross-Origin Resource Sharing) 跨域资源共享,管理跨源请求。而跨域资源共享是有益的。今天的我们创建的大多数网站加载资源从网络的各个不同的地方, 当我们发送 GET 请求时,大多数情况浏览器头会 Access-Control-Allow-Origin: * 。意思是能够共享资源在任何域名下。否则就是只能在特定的情况下了。 当请求时以下时,将在原始请求前先进行标准预请求,使用 OPTIONS 头,
浏览器访问外部资源时会出现下面这种情况
而我们想要的效果是这种情况.
我们可以在 HTTP 请求头加上 CORS 标准。
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Request-Headers
Access-Control-Request-Method
Origin
当我们发送 GET 请求时,大多数情况浏览器头会 Access-Control-Allow-Origin: * 。意思是能够共享资源在任何域名下。否则就是只能在特定的情况下了。
app.use(cors()); 当请求时以下时,将在原始请求前先进行标准预请求,使用 OPTIONS 头,
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH 当我们发的预处理请求并指示原始请求是否安全,如果指定原始请求时安全的,则它将允许原始请求。否则拒接。
options:与 head 类似,是客户端用于查看服务器的性能 。JavaScript 的 XMLHttpRequest 对象进行 CORS 跨域资源共享时,就是使用 OPTIONS 方法发送嗅探请求,以判断是否有对指定资源的访问权限
Server Proxy: 服务器代理,当你需要有跨域请求额操作时发送给后端,让后端帮你带为请求,然后将最后的获取的结果发送给你。
document.domain + iframe
基于 iframe 实现的跨域要求两个域具有 aa.xx.com, bb.xx.com 这种特点,也就是两个页面必须属于一个基础域(例如都是 xxx.com,或是 xxx.com.cn),使用同一协议(例如都是 http)和同一端口(例如都是 80),这样在两个页面中同时添加 document.domain,就可以实现父页面调用子页面的函数,当然这种方法只能解决主域相同而二级域名不同的情况。 代码如下:
6.1、通过 location.hash 跨域
以下三种都是通过 iframe 方式来,我们根据 iframe 能够在浏览器下能够跨域的特点,进行通信。
在 url 中,http://www.a.com#la 的 "#la" 就是 location.hash,改变 hash 值不会导致页面刷新,所以可以利用 hash 值来进行数据的传递,当然数据量是有限的。
6.2 通过 window.name 跨域 window 对象有个 name 属性,该属性有个特征:即在一个窗口(window)的生命周期内, 窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的。window.name 属性的 name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。
如果是与 iframe 通信的场景就需要把 iframe 的 src 设置成当前域的一个页面地址。
6.3 windows.postMessage
otherWindow.postMessage(message, targetOrigin, [transfer]);
接收数据:data、type:类型、source:对象、origin:源;
websocket
WebSocket 是一种通信协议,使用 ws://(非加密)和 wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
canvas 操作图片的跨域问题
最后更新于
这有帮助吗?