You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CORS 需要浏览器和服务器同时支持才生效,前端不需要做任何操作。当浏览器发现发送的请求是跨域请求时,会自动在请求头加上Origin字段表明当前的域(协议+域名+端口号),非简单请求还会先发送一次额外请求进行预检,但这都是浏览器自动完成的,用户和前端并不需要增加操作。而请求之后服务器也会返回一个http响应,如果返回的响应中带有Access-Control-Allow-Origin,并与上面请求头的Origin相匹配的话,那么即允许跨域;否则抛出错误被 XMLHttpRequest 的onerror回调函数捕获。注意,这种错误状态码可能是200,所以不能通过状态码去判断。
一、同源策略
什么是浏览器的同源策略?浏览器出于安全考虑,只允许相同域下的资源进行交互,不同源下的脚本在没有明确授权的情况下,不能读写对方的资源,这就是同源策略。
而所谓的同源,指的是
协议、域名、端口号
相同。举个例子:http://github.com:80
,其中http://
就是协议,而github.com
就是域名,80
是端口(默认端口可以省略)。只有这三个完全相同才算同源,任何一个不同都不算。假设当前的网址是http://www.a.com:80/a/index.js
,同源情况如下:https://www.a.com:80
不同源(协议不同)http://www.a.com:90
不同源(端口号不同)http://a.com:80
不同源(域名不同)http://abc.a.com:80
不同源(域名不同)http://www.b.com:80
不同源(域名不同)http://192.110.110.110:80
不同源(192.110.110.110
是www.a.com
对应的ip也认为域名不同)http://www.a.com:80/b/index.js
同源(只要前部分相同,后面不同文件夹也可以)如果两个网站不同源,则交互会受到限制,具体如下:
而为啥要有这个策略限制呢?试想一下,在一个窗口刚登录过银行网站
http://www.bank.com
,然后又切换到另一个页面http://hacker.com
,如果这个与银行非同源页面能够访问到银行页面的cookie,而cookie里却保存着银行的账号和密码,这是一件非常危险的事情。二、跨域方案
虽然这个同源策略是为了安全而诞生的。但有时候开发我们却需要有一些跨域请求或操作。比如使用第三方服务而需要请求第三方服务器,这就是所谓的跨域。
那如何避开浏览器的同源策略实现跨域呢?此处主要整理了八种方案,主要分为三类:
(一)AJAX 请求的跨域
1. JSONP
JSONP是跨域中非常常见的一种形式,他支持所有老版的浏览器。其主要是利用页面上请求
<script>
脚本不受同源策略限制的特性,来实现跨域。主要步骤如下:callback=funcName
。<script>
元素,src指定为上面增加了callback的url。(1) 原生版本
(2)jquery的ajax版本
JSONP的优点和缺点如下:
2. CORS
所谓CORS,全称
Cross-Origin Resource Sharing
跨域资源共享,是一个W3C标准,专门用于解决 AJAX 的跨域问题。它允许浏览器向跨源服务器发出 XMLHttpRequest 请求(如果没有增加 CORS 支持则不能向非同源发送 AJAX 请求,会被浏览器拦截)。CORS 需要浏览器和服务器同时支持才生效,前端不需要做任何操作。当浏览器发现发送的请求是跨域请求时,会自动在请求头加上Origin字段表明当前的域(
协议+域名+端口号
),非简单请求还会先发送一次额外请求进行预检,但这都是浏览器自动完成的,用户和前端并不需要增加操作。而请求之后服务器也会返回一个http响应,如果返回的响应中带有Access-Control-Allow-Origin
,并与上面请求头的Origin相匹配的话,那么即允许跨域;否则抛出错误被 XMLHttpRequest 的onerror回调函数捕获。注意,这种错误状态码可能是200,所以不能通过状态码去判断。所以,可以见得,实现CORS的关键是后端要增加相应的字段。具体见下面的例子(还可以返回更多的头信息如
Access-Control-Allow-Credentials
等,此处只写了关键的一步):CORS 的优缺点如下:
3. WebSocket
WebSocket 是HTML5一种新的协议。它实现了浏览器与服务器的双向通信,同时不受同源策略限制,允许跨域通讯,使用ws://(非加密)和wss://(加密)作为协议前缀。
一般使用WebSocket,我偏向使用socket.io。后者对前者的API进行了封装,使其更易用。前者只支持 IE 10 以上等新版浏览器,而后者兼容了旧版浏览器。所以此处增加个socket.io的例子。
(二)iframe实现跨域
iframe的跨域实现,主要有document.domain、location.hash、window.name、postMessage四种方案,下面我们一一解析。
1. document.domain
这种方法适合一级域名相同,二级域名不同的情况下使用。域名相关见如下:
2. location.hash
location.hash
获取的是当前地址栏url的片段识别符,即http://example.com/x.html#fragment
里#
及后面部分#fragment
。由于单纯改变片段识别符并不会导致页面刷新,所以我们可以利用这个特性来让父子窗口互相传值。如果两个窗口不在同一个域下, IE、Chrome 不允许子窗口修改 parent.location.hash 的值,所以要借助于一个和父窗口同域的页面来实现修改 hash 值。
3. window.name
window.name
是窗口的名字,每个子窗口也有自己的window
和window.name
。只能保存字符串,如果写入的值不是字符串,会自动转成字符串。其特殊之处在于只要窗口不关闭,这个属性便不会消失,且储存容量可高达几MB。如果加载了a.com之后写入window.name
,窗口不关闭重新加载b.com,此时window.name
还是a.com写入的值;窗口关闭window.name
清除。利用这个特性,我们可以在当前域下创建一个目标域的子窗口,目标域的数据放在
window.name
,加载完成后再让子窗口跳到一个父域相同的空白代理页(window.name
还是不变),获取这个代理页的window.name
赋值给父域即可。注意,一定要有与父域同域的代理页,否则父域是无法直接获取子窗口的window.name
的;另外重新跳转页面会触发onload,要注意避免死循环,可以加个loaded标记。4. window.postMessage
可以无论
hash
还是window.name
都是利用一些特性来绕个弯达到目的,均属破解。为了解决该问题,HTML5 引入了一个新API跨文档通信 API(Cross-document messaging),无论两个窗口页面是否同源,都可以通过调用window.postMessage(content, target)
来进行通信。其中target为协议域名端口号,可以设置“ * ”代表向全部窗口发送,也可以指定“ / ”代表当前域。父子窗口通过监听message事件可以获得来源方的这些信息event.origin(源网址)
、event.data(携带的数据)
、event.source(发送消息方的窗口)
。(三)服务器代理
最后一类实现跨域的方法是通过架设代理服务器来实现跨域。即**先请求同源服务器,再由同源服务器请求外部服务器。**由于请求的是同源服务器,所以不受浏览器同源策略限制,而服务器之间的请求也没有同源策略这一说,所以以此达到跨域目的。由于对服务器方面了解并不是很深,此处就不做展开,有兴趣可以自行了解下。
至此,八种跨域的方式终于讲完了(写了好久...吃掉每个小点再讲明白真不容易,可能还有图片ping啥的,先躺倒休息会...),以上全部都是简单案例可根据需求进行优化扩充,如有错漏,欢迎指出!
The text was updated successfully, but these errors were encountered: