前言:
目录:
"JS 跨域" 其实是一个 "伪名词"。跨域实际上是浏览器基于安全考虑做出的 "同源策略限制"。
这个策略早在 1995 年便由 Netscape 公司引入浏览器中,目前所有浏览器都实行这个机制。
"同源策略" 的基本规则是:如果协议 (protocol)、端口 (port)、主机 (host) 等,若有一个不同,浏览器便会限制不同域之间的通信与数据交换。
以 http://www.example.com 为例:
域名 | 是否跨域 | 原因 |
http://www.example/com/js/b.js | 否 | pathname |
https://www.example.com | 是 | protocol |
http://www.example:80.com | 是 | port |
http://a.example.com | 是 | host |
http://www.sample.com | 是 | host |
http://127.0.0.1 | 是 | host |
* 浏览器只会根据 URL 进行判断是否跨域,而不会去判断两个域名是否对应一个 IP,更或者 IP 与域名之间的判断。
* 来自 about:blank,javascript: 和 data:URLs 中的内容,继承了将其载入的文档所指定的源,因为它们的 URL 本身未指定任何关于自身源的信息,
所以也并不会跨域。
同源策略可以很好的保护我们网页不会被随意的篡改,我们的信息不被任意获取,但是在某些情况下也会是我们工作的一个障碍。
比方说,现在公司有两个域名:http://www.example.com 以及 http://www.sample.com。
这两个域名下的页面需要相互进行通信,那么想办法跳过同源限制进行跨域操作,就是我们前端必须掌握的技术。
同源策略对前端功能的影响主要有以下几点:
1. Cookie、Storage、IndexedDB 等无法读取。
2. DOM 无法操作
3. AJAX 无法请求。
就目前而言,前端可以实现跨域的技术,主要有以下几点:
对于两个域名,如果有相同主域. 例如 http://b.example.com 与 http://a.example.com 这两个域名,有相同的主域 example, 在这种情况下,我们可以通过指定 domain 来实现两个页面之间的相互通信。
准备:
----------------------------
example.com : A.html
example.com : B.html
在 A.html 放入一个 iframe 把 B.html 内嵌到 A.html 中.
----------------------------
示例:
A.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < title > Document < /title>
- 6 </head > 7 < body > 8 < p > This is A html < /p>
- 9 <iframe src="http:/ / b.st.cn "></iframe>
- 10 <button>set B.html BackGround</button>
- 11 </body>
- 12 </html>
- 13 <script>
- 14
- 15 var ifr = document.getElementsByTagName('iframe')[0],
- 16 btn = document.getElementsByTagName('button')[0];
- 17
- 18 document.domain = 'example.com';
- 19
- 20 load(ifr,function(){
- 21 btn.onclick=function(){
- 22 ifr.contentWindow.document.body.style.background = 'red';
- 23 }
- 24 });
- 25
- 26 </script>"
B.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < title > Document < /title>
- 6 </head > 7 < body > 8 < p > This is B html < /p>
- 9 <button>set A.html BackGround</button > 10 < /body>
- 11 </html > 12 < script > 13
- var btn = document.getElementsByTagName('button')[0];
- 14 15 document.domain = 'example.com';
- 16 17 btn.onclick = function() {
- 18 window.top.document.body.style.background = 'blue';
- 19
- }
- 20 < /script>/
PS : 关于 load() 方法的说明,见:《Iframe 功能详解》
对于主域相同而子域不同的两个页面,设置 domain 只可以实现框架之间的交互,如果想进行 AJAX 请求的话,依然是会有跨域限制。
解决的办法是在要 XMLHttpRequest 请求的目标页面下再增加一个代理 proxy.html。然后设置当前页面与 proxy.html 的 document.domain 。最后再用代理页面去 AJAX 请求目标页面。
示例:
A.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <p>This is A html</p > 8 < /body>
- 9 </html > 10 < script src = "jquery.min.js" > </script>
- 11 <script>
- 12
- 13 document.domain = 'example.com';
- 14
- 15 function crossDomainAjax(url,rqurl,callback,id){
- 16
- 17 var ifr = document.createElement('iframe'),
- 18 rq = function(){
- 19 var $ = ifr.contentWindow.$;
- 20 $.get(rqurl,function(data){
- 21 callback && callback($(data));
- 22 });
- 23 };
- 24
- 25 ifr.src = url;
- 26 ifr.id = id;
- 27
- 28 if(window.attachEvent){
- 29 ifr.attachEvent('onload',rq);
- 30 }else{
- 31 ifr.onload= rq;
- 32 }
- 33
- 34 document.body.appendChild(ifr);
- 35 return false;
- 36
- 37 }
- 38
- 39 crossDomainAjax('http:/ / b.example.com / proxy.html ','http: //b.example.cn/index.html',function(data){});
- 40 41 < /script>/
proxy.html - Code :
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < title > Document < /title>
- 6 </head > 7 < body > 8...9 < /body>
- 10 </html > 11 < script src = "jquery.min.js" > </script>
- 12 <script>
- 13 document.domain = 'example.com';
- 14 </script >
其中 crossDomainAjax 方法是封装好的用于相同主域,不同子域的 AJAX 请求。
如果对于域名完全不同的两个页面,便无法再通过设置 domain 的方式来实现两个页面之间的通信。而使用 iframe + hash 的组合方式便可以解决不同域之间的页面通信或数据交换,这是因为改变 hash 并不会导致页面刷新,所以可以利用 location.hash 来进行数据的传递。
iframe+hash 实现跨域通信的逻辑是,在 example.com 域下有一个 A.html,在 sample.com 下有一个 B.html, 在 A.html 中放入一个 iframe ,这个 iframe 的 src 指向的是 B.html,并且在 url 后面附加 url 参数,这样 A.html 内嵌套 B.html 的过程就实现了一个请求的发送,B.html 中会去读取发送过来的参数,然后执行相应的处理,处理完成后 B.html 也会插入一个 Iframe, 这个 iframe 嵌入的页面是与 A.html 同域的代理页面。并将处理的结果附加在代理页面的 hash 中,最终再由代理页面将处理的结果传递给 A.html。
使用 iframe+hash 来跨域进行数据传递,是有很多弊端的,比如会导致历史记录的产生,数据量限制、内容的直接暴露(URL 内容是可见的)、传输的数据类型.... 所以,这里我更加推荐下一种要说到的跨域方式 : iframe + name
具体流程如下图:
准备:
----------------------------
example.com :A.html 、proxy.html
sample.com :B.html
----------------------------
示例:
A.html - Code :
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < title > A.html < /title>
- 6 </head > 7 < body > 8 < button > SendMessage < /button>
- 9 <script>
- 10 var obtn = document.getElementsByTagName('button')[0];
- 11
- 12 function handle(ifr) {
- 13
- 14 if (window.addEventListener) {
- 15 window.onhashchange = function() {
- 16 hash = location.hash.split('#')[1];
- 17 if(hash){
- 18 console.log('接收到了数据:'+hash);
- 19 document.body.removeChild(ifr);
- 20 this.onhashchange=null;
- 21 location.hash = '';
- 22 }
- 23 };
- 24
- 25 } else {
- 26
- 27 (function() {
- 28 var timer = null;
- 29 setTimeout(function() {
- 30 hash = location.hash.split('#')[1];
- 31 if (hash) {
- 32 console.log('接收到了数据:'+hash);
- 33 document.body.removeChild(ifr);
- 34 timer = null;
- 35 location.hash = '';
- 36 return false;
- 37 }
- 38 timer = setTimeout(arguments.callee, 1000);
- 39 }, 0);
- 40 }());
- 41 }
- 42 document.body.appendChild(ifr);
- 43 }
- 44
- 45
- 46 obtn.onclick = function() {
- 47 var ifr = document.createElement('iframe'),
- 48 iframe = document.getElementById('hashCorssDomain');
- 49
- 50 if(iframe){return false} / / 防止并发操作51 52 ifr.style.display = "none";
- 53 ifr.id = 'hashCorssDomain';
- 54 ifr.src = 'http://sample.com/B.html?openHash=true&hash=msg';
- 55 handle(ifr);
- 56 57
- }
- 58 59 < /script>
- 60 </body > 61 < /html>/
B.html - Code :
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < title > Document < /title>
- 6 </head > 7 < body > 8 < p > This is B.html < /p>
- 9 </body > 10 < /html>
- 11 <script>
- 12 var openHash = getUrl('openHash'),
- 13 hash = getUrl('hash'),
- 14 result = '';
- 15
- 16 function getUrl(key, type) {
- 17 var type = type || '?';
- 18 var keyArr;
- 19 if (type == '?') {
- 20 keyArr = location.search.substring(1).split('&');
- 21 } else {
- 22 keyArr = location.hash.substring(1).split('&');
- 23 }
- 24 for (var i = 0; i < keyArr.length; i++) {
- 25 if (key == keyArr[i].split('=')[0]) {
- 26 return keyArr[i].split('=')[1];
- 27 }
- 28 }
- 29 return '';
- 30 }
- 31
- 32 if (openHash && hash) {
- 33
- 34 if (hash == '123') {
- 35 result = '456';
- 36 } else {
- 37 result = 'abc';
- 38 }
- 39
- 40 try {
- 41 top.hash = hash + '#' + (~~Math.random() * 1e6) + '=' + result;
- 42 } catch (e) {
- 43 var ifr = document.createElement('iframe');
- 44 ifr.src = 'http:/ / example.com / proxy.html ' + '#'+ hash + ~~(Math.random() * 1e6 ) + ' = ' + result;
- 45 ifr.style.display = "none";
- 46 document.body.appendChild(ifr);
- 47 }
- 48 }
- 49
- 50 </script>'
proxy.html - Code :
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <p>This is Proxy.html</p > 8 9 < /body>
- 10 </html > 11 < script > 12
- var result = location.hash.split('#')[1];
- 13
- if (result) {
- 14 top.location.hash = result;
- 15
- }
- 16 < /script>/
以下代码是完成 iframe + hash 跨域的完整示例,这里为了后期使用,特意对上面代码进行封装:
- function hashCrossDomain(params) {
- var callback = params.callback ||
- function() {},
- urlparams = params.urlparams || '',
- type = params.type || 'accept',
- href = params.url || '',
- data = '';
- function handle(ifr) {
- if (window.addEventListener) {
- window.onhashchange = function() {
- hash = location.hash.split('#')[1];
- if (hash) {
- callback(hash);
- document.body.removeChild(ifr);
- this.onhashchange = null;
- location.hash = '';
- }
- };
- } else { (function() {
- var timer = null;
- setTimeout(function() {
- hash = location.hash.split('#')[1];
- if (hash) {
- callback(hash);
- document.body.removeChild(ifr);
- timer = null;
- location.hash = '';
- return false;
- }
- timer = setTimeout(arguments.callee, 1000);
- },
- 0);
- } ());
- }
- document.body.appendChild(ifr);
- }
- function getUrl(key, type) {
- var type = type || '?';
- var keyArr;
- if (type == '?') {
- keyArr = location.search.substring(1).split('&');
- } else {
- keyArr = location.hash.substring(1).split('&');
- }
- for (var i = 0; i < keyArr.length; i++) {
- if (key == keyArr[i].split('=')[0]) {
- return keyArr[i].split('=')[1];
- }
- }
- return '';
- }
- if (type === 'send') {
- var ifr = document.createElement('iframe'),
- iframe = document.getElementById('hashCorssDomain');
- if (iframe) return false; // 防止并发操作
- ifr.style.display = "none";
- ifr.id = 'hashCorssDomain';
- ifr.src = href + '?' + urlparams;
- handle(ifr);
- } else {
- openHash = getUrl('openHash');
- data = getUrl('hash');
- if (openHash && data) {
- try {
- top.hash = hash + '#' + (~~Math.random() * 1e6) + '=' + callback(data);
- } catch(e) {
- var ifr = document.createElement('iframe');
- ifr.src = href + '#' + data + ~~ (Math.random() * 1e6) + '=' + callback(data);
- ifr.style.display = "none";
- document.body.appendChild(ifr);
- }
- }
- }
- }
使用说明:
发送方 (A.html) 调用方法:
- document.getElementsByTagName('button')[0].onclick = function() {
- hashCrossDomain({
- 'type': 'send',
- 'url': 'http://sample.com/B.html',
- 'urlparams': 'openHash=true&hash=msg',
- 'callback': function(data) {
- console.log('返回的数据' + data);
- }
- })
- }
参数说明:
type : 用于设置当前页面属性,值为 "send" 表示请求方页面。值为 "accept" 表示接收方页面,缺省值表示接收方。
url : 表示发送目标的 url,(这里便是 B.html 的地址)
urlparams : 表示发送的 url 参数,值为 "openHash=true&hash=msg" 格式,其中 openHash 表示以 hash 值方式进行跨域,而 hash 的值则是具体发送的值。
callback : 响应结果的回调函数。
接收方 (B.html) 调用的方法:
- hashCrossDomain({
- 'url': 'http://example.com/proxy.html',
- 'callback': function(data) {
- if (data == 'msg') {
- return 'abc';
- } else {
- return '123';
- }
- }
- });
参数说明:
url : 表示发送目标的 url,(这里便是 B.html 的地址)
callback : 接收发送的参数,并进行特定的处理,返回处理的结果,该结果会再次作为 url 参数发送到代理页面 proxy.html。
name 是 window 对象的一个属性,这个属性有一个特点,只要在一个窗口 (window) 的生命周期内,该窗口载入的所有页面,不论怎么改变,每个页面都能对 window.name 进行读写,并且支持非常长的 name 值(~2MB) 例如,在一个窗口内,我 A 页面设置的 window.name = 'this is A.html',然后通过 location.href = 'B.html' , 再输出 window.name 依然是'this is A.html',因此利用这样一个特性,我们就可以实现在不同的页面中进行数据交换或通信。
iframe + name 实现的跨域方式与 iframe + hash 原理基本上都是一致的。不过相比之下,window.name 更加实用,因为通过 window.name 不会随便将数据暴露出来,页面也不会加入历史记录,而且可存储的内容更加的多。同样的,通过 window.name 方式跨域的不足之处就是目前 JS 方面并没有好的检测方法,所以只能通过轮询的方式监测 name 值的更改。
具体流程见下图:
准备:
----------------------------
example.com :A.html 、 proxy.html
sample.com : B.html
----------------------------
使用 name 进行跨域操作的封装函数:
- function nameCorssDomain(params) {
- var callback = params.callback ||
- function() {},
- urlparams = params.urlparams || '',
- type = params.type || 'accept',
- href = params.url || '',
- result = '',
- name = '',
- data = '',
- frame = '',
- ifr = '';
- function handle() { (function() {
- var timer = setTimeout(function() {
- if (window.name) {
- iframe = document.getElementById('nameCorssDomain');
- iframe.contentWindow.document.write('');
- iframe.contentWindow.close();
- document.body.removeChild(iframe);
- callback(window.name);
- window.name = '';
- clearTimeout(timer);
- timer = null;
- }
- setTimeout(arguments.callee, 1000);
- },
- 0)
- } ())
- }
- function getUrl(key, type) {
- var type = type || '?';
- var keyArr;
- if (type == '?') {
- keyArr = location.search.substring(1).split('&');
- } else {
- keyArr = location.hash.substring(1).split('&');
- }
- for (var i = 0; i < keyArr.length; i++) {
- if (key == keyArr[i].split('=')[0]) {
- return keyArr[i].split('=')[1];
- }
- }
- return '';
- }
- if (type === 'send') {
- ifr = document.createElement('iframe');
- ifr.style.display = 'none';
- ifr.id = 'nameCorssDomain';
- if (window.attachEvent) {
- ifr.attachEvent('onload',
- function() {
- handle();
- });
- } else {
- ifr.onload = function() {
- handle();
- }
- }
- ifr.src = href + '?' + urlparams;
- document.body.appendChild(ifr);
- } else {
- name = getUrl('openName');
- data = getUrl('name');
- name && data ? location.href = href + '?' + callback(data) : '';
- }
- }
使用格式:
- nameCorssDomain({
- 'type': 'send',
- 'url': 'http://sample.com/B.html',
- 'data': 'openName=true&name=123',
- 'callback': function() {}
- });
参数说明:
type : 用于指定当前页面是发送请求页面 (send) 还是 接收请求处理页面 (accept)。
url : 用于指定链接到的目标页面。对于请求页面来说,url 是接收请求处理页面,对于接收请求处理页面而言,url 则是代理页面 proxy.html。
urlparams : 表示发送的 url 参数,值为 "openName=true&name=123" 格式,其中 openName 表示以 name 值方式进行跨域,而 name 属性的值则是具体发送的值
data : 发送的 url 数据参数, 其中 openName = true 表达开启 name 跨域操作,而 name = 123, 则是具体传输的数据。
callback : 处理结果的回调函数。
具体示例:
A.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <button>Send Message</button > 8 < /body>
- 9 </html > 10 < script > 11
- var oBtn = document.getElementsByTagName('button')[0];
- 12 13 oBtn.onclick = function() {
- 14 nameCorssDomain({
- 15 'type': 'send',
- 16 'url': 'http://sample.com/B.html',
- 17 'urlparams': 'openName=true&name=123',
- 18 'callback': function(data) {
- 19 console.log(data);
- 20
- }
- 21
- });
- 22
- }
- 23 24 < /script>/
B.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <script>
- 8
- 9 nameCorssDomain({
- 10 'url':'http:/ / example.com / proxy.html ',
- 11 'callback ':function(data){
- 12 if(data == '123456 '){
- 13 return '789 ';
- 14 }else{
- 15 return 'abcdef ';
- 16 }
- 17 }
- 18 });
- 19
- 20 </script>
- 21 </body>
- 22 </html>'
Proxy.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <p>This is Proxy.html</p > 8 9 < /body>
- 10 </html > 11 < script > 12
- var result = location.search.split('?')[1];
- 13
- if (result) {
- 14 top.name = result;
- 15
- }
- 16 < /script>/
HTML5 引入了一个跨文档消息传输(Cross-document messaging)的 API。
这个 API 为 window 对象新增了个 postMessage 方法,可以使用它来向其它的 window 对象(窗口)发送消息,无论这个 window 对象是属于同源或不同源。
同时这个 API 也为 window 新增了一个 onmessage 事件,用于接收其它窗口发来的消息内容。
需要注意的是,这个消息内容只能为字符串,但是可以设置为 json 格式的数据。
目前 IE8+、FireFox、Chrome、Opera 等浏览器都已经支持 window.postMessage 方法。
详细支持情况如下:
发送消息格式:
- otherWindow.postMessage(message, targetOrigin)
功能:向指定的窗口 otherWindow 发送跨域文档消息
参数:
otherWindow: 对接收信息页面的 window 的引用。可以是页面中 iframe 的 contentWindow 属性;window.open 的返回值;通过 name 或下标从 window.frames 取到的值。
message: 所要发送的数据,string 类型。
targetOrigin: 用于限定指定域中的 otherWindow,"*" 表示不作限制,常规值是一个 url。
接收消息格式:
- otherwindow.onmessage = function(event) {
- // 常用的event属性。
- //event.source:发送消息的窗口
- //event.origin: 消息发向的网址
- //event.data: 消息内容
- };
准备:
----------------------------
example.com : A.html
sample.com : B.html
----------------------------
示例:
A.html - Code:
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <iframe src="http:/ / hd.qy.iwgame.test / B.html " frameborder="0 "></iframe>
- 8 <script>
- 9 var ifr = document.getElementsByTagName('iframe')[0];
- 10 ifr.onload=function(){
- 11 window.frames[0].postMessage('msg','http://hd.qy.iwgame.test');
- 12 }
- 13 </script>
- 14 </body>
- 15 </html>"
B.html - Code:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- Document
- </title>
- </head>
- <body>
- <script>
- window.onmessage = function(e) {
- if (e.origin === 'http://hd.tzj.iwgame.test') { // 规定只接收指定的窗口消息。
- document.body.style.background = 'red';
- }
- }
- </script>
- </body>
- </html>
其工作流程如图所示:
>>> 更多关于 JSONP 的相关知识以及封装方法,请参考我的另一篇博客:
CORS(Cross-Origin Resource Sharin) 即跨源资源分享。它已经被 W3C 标准化。
CORS 是 AJAX 实现跨域请求的根本解决方案,它允许一个域上的页面向另一个域提交跨域请求。而实现这个功能非常简单,只需要服务端设置响应头即可。
CROS 于 JSONP 相比,前者只能发送 GET 请求,而 CORS 允许任何类型的请求。但是 CORS 也有其不足之处,我个人认为主要有两点:
1. CROS 带来的潜在的安全性问题。
2. CORS 需要浏览器和服务器同时支持。
CORS 在浏览器中的兼容性见下图:
具体示例:
准备:
----------------------------
example.com : getName.php
sample.com : index.html
----------------------------
现在在两个不同的域名中,分别有一个 php 文件,和一个 html 文件,现在我们要实现的需求是,在 sample.com 域下的 index.html 去 AJAX 请求 example.com 域下的 php 文件。
index.html - Code -
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < title > Document < /title>
- 6 </head > 7 < body > 8 < button > click < /button>
- 9 <script src=".../jquery.min.js "></script>
- 10 <script>
- 11 var oBtn = document.getElementsByTagName('button')[0];
- 12 oBtn.onclick=function(){
- 13 $.ajax({
- 14 type:'post',
- 15 url:'http://example.com/getName.php',
- 16 data:{'name':'shenguotao'},
- 17 success:function(data){
- 18 alert(data);
- 19 }
- 20 });
- 21 }
- 22 </script>
- 23 </body>
- 24 </html>"
getName.php - Code -
- 1 php 2 header("Access-Control-Allow-Origin:*");
- 3 $name = $_POST['name'];
- 4 echo $name;
- 5 ? >
运行结果见下图:
在上面的例子中,后端只要设置了响应头 header("Access-Control-Allow-Origin:*") ,就表示该接口可以被任意域进行请求。
在上面的例子中,Origin 字段用来说明,本次请求来自哪个源。服务器根据这个值,决定是否同意这次请求。如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,被 XMLHttpRequest 的 onerror 回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是 200。这里用 "*" 表示匹配任意的 "源",如果只匹配特定的源,那么将 "*" 改成特定域的地址即可。
如果是 java 环境可以这样设置: response.setHeader("Access-Control-Allow-Origin", "*") 。
需要注意的是在 CORS 请求中,默认是不发送 Cookie 和 HTTP 认证信息的。如果要把 Cookie 发到服务器,一方面要服务器同意并设定 Credentials 字段。
- header("Access-Control-Allow-Credentials: true");
另一方面,开发者必须在 AJAX 请求中打开 withCredentials 属性。
- var xhr = new XMLHttpRequest();
- xhr.withCredentials = true;
如果是 AJAX, 则:
- $.ajax({
- type: "POST",
- url: "http://xxx.com/api/test",
- dataType: 'jsonp',
- xhrFields: {
- withCredentials: true
- },
- crossDomain: true,
- success: function() {},
- error: function() {}
- });
需要注意的是,如果要发送 Cookie,Access-Control-Allow-Origin 就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨源)原网页代码中的 document.cookie 也无法读取服务器域名下的 Cookie。
反向代理实际上就是欺骗浏览器,明的告诉浏览器这个后台接口是从同域下请求到的,但背后 web 服务会将后台接口实际所在的域与浏览器当前访问的域做了某种类似于绑定的操作。
代理实现跨域的本质就是同源策略只是浏览器单方面的限制。
这里我就以 nginx 为例。
nginx 配置参数如下:
- server {
- listen 80;
- server_name example.com;
- index index.htm index.html;
- location / htmlrpt {
- proxy_pass sample.com / htmlrpt; // 如果是 htmlrpt 目录下的资源,则转向 sample.com/htmlrpt 这个域名下请求。
- proxy_redirect off;
- proxy_set_header Host $host;
- proxy_set_header X - Real - IP $remote_addr;
- proxy_set_header X - Forwarded - For $proxy_add_x_forwarded_for;
- }
- location / {
- root D: \\workspace\\git\\src\\main\htmlrpt;
- }
- }
通过以上设置,我们在 example.com 域下请求 example.com/htmlrpt/index.php ,实际上 web 服务会从 sample.com/htmlrpt 下调取该接口。
WebSocket 是 HTML5 的一种新技术。它实现了浏览器与服务器全双工即时通信 (full-duplex)。在过去如果想实现同样的即时通信,一般都是以轮询的方式,比如每隔 1 秒钟,发送一次 http 请求,然后由服务器返回最新的数据给客户端浏览器,这种不断轮询的方法明显的缺点就是,需要不断的向服务器发送请求,并且每次都要携带很大的 request 头信息,而实际的数据,可能只是很小的一部分。
WebSocket 连线过程中,需要通过浏览器发出 WebSocket 连线请求,然后服务器发出回应,这个过程通常称为。"握手" (handshaking) 在 WebSocket 方式下,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就可以直接互相传送数据。使用 WebSocket,为我们实现即时服务带来了两大好处:
1. Header
互相沟通的 Header 是很小的 - 大概只有 2 Bytes
2. Server Push
服务器的推送,服务器不再被动的接收到浏览器的 request 之后才返回数据,而是在有新数据时就主动推送给浏览器。
在 WebSocket 的消息头中,也有一个与 CROS 含义相同的头字段,即:Sec-WebSocket-Origin: null,它用于设置指定的域才能与服务器进行通信, 而我们前端就是利用 WebSocket 这种可以与服务器通信的功能来实现跨域!当然通过这种方式跨域,有点像大炮打蚊子一般,但是何尝不是一种方案!
开发者如果想使用 WebSocket 需要知道两点情况,一是浏览器是否支持 WebSocket API , 二是后台服务器是否部署了 WebSocket 服务。这两者缺一不可。前端对于 WebSocket 的操作,也就以下几个方面:
· 开启与关闭 WebSocket 链接
· 发送与接收消息
· 错误处理
前端示例代码:
- if ('WebSocket' in window) {
- var connection = new WebSocket('ws://example.com');
- /* 创建一个WebSocket对象的实例。并与指定WebSocket服务器进行连接,而这个连接的过程就是握手的过程
- 此时 connection 对象上有一个 readyState属性,用于表示当前连接的状态。
- readyState 状态值:
- 0: 正在连接
- 1: 连接成功
- 2: 正在关闭
- 3: 连接关闭
- */
- function open() {
- console.log('服务器与WebSocket服务链接成功');
- }
- function close() {
- console.log('服务器与WebSocket服务链接关闭');
- }
- /* 当readyState属性的值为1,便会触发 connection对象的open事件
- 当readyState属性的值为2,便会触发 connection对象的close事件,其中若值为3则表示关闭成功
- */
- connection.onopen = open();
- connection.onclose = close();
- /* 连接成功后,可以通过 send 方法发送消息 */
- connection.send('hellow WebSocket!');
- /* 客户端接收到服务端发送过来的消息后,WebSocket 便会触发message事件 */
- connection.onmessage = function(e) {
- console.log(e.data);
- }
- /* 如果WebSocket出现错误 , 便会触发error事件*/
- connection.onerror = function(e) {
- console.log(e.data);
- }
- }
这里给一个 webSocket 在线测试网站,有兴趣的童鞋可以去看看
使用 flash 方式进行跨域,其原理就是将上述 iframe 方式中用到的代理页面 (proxy.html) 替换成 flash 而已。
相比于浏览器 flash 有着自己的一套安全策略。对于 flash 而言只要被请求域的根目录下有一个 crossdomain.xml 的文件,并且其中规定了当前请求域是被允许状态,那么 flash 便可以请求指定的资源。
加上 javaScript 代码是可以与 flash 代码进行相互调用,所以我们就可以用 js 发起请求,然后让 flash 代理请求,并在被请求 "域" 的根目录下放置好 crossDomain.xml 文件,最终 flash 完成请求获得结果数据,再通过调用页面的 JS 回调函数方式将结果发送到页面上。
具体流程如图所示:
具体示例:
准备:
----------------------------
example.com : index.php
crossDomain.xml
sample.com : ajaxcdr.js
ajaxcdr.swf
index.html
----------------------------
这里我们直接使用封装好的 flash 跨域组件 ajaxcdr。其中 ajaxcdr.js 可以帮助我们生成 flash,并且向 flash 中发送请求。而 ajaxcdr.swf 则可以代理我们的请求
index.php - Code -
- 1 php 2 header("Cache-Control: no-cache, must-revalidate");
- 3 $name = $_POST['name'];
- 4 echo $name;
- 5 ? >
crossDomain.xml - Code -
- 1 < ?xml version = "1.0"encoding = "UTF-8" ? >2 < cross - domain - policy > 3 < allow - access - from domain = "*" / >4 < /cross-domain-policy>/
index.html - Code -
- 1 < !DOCTYPE html > 2 < html lang = "en" > 3 < head > 4 < meta charset = "UTF-8" > 5 < /head>
- 6 <body>
- 7 <button>send Request</button > 8 < /body>
- 9 </html > 10 < script src = "ajaxcdr.js" > </script>
- 11 <script>
- 12 var button = document.getElementsByTagName('/
来源: http://www.cnblogs.com/HCJJ/p/6128457.html