本页二维码,扫一扫分享到朋友圈
朋友圈

浏览器javascript跨域问题解决办法


阅读159 评论0 赞 47返回首页    go 编程与技术  go javascript


前言

之前在一个项目中,调用了别的部门的接口,但是由于存在跨域问题,只能进行简单的本地数据模拟调试,然后再把资源给对方进行联调。这样的方法肯定不是最佳方案,正好最近看到一篇文章,说到的就是跨域问题的解决办法,所以进行了学习和整理。


同源政策

首先说说同源政策,最初,它的含义是指,A网页设置的Cookie,B网页不能打开,除非这两个网页“同源”。所谓“同源”,就是三个相同:

  1. 协议相同

  2. 域名相同

  3. 端口相同

同源政策的目的就是为了防止信息泄露。

如果非同源,共有三种行为会受到限制:

  1. Cookie、LocalStorage和IndexDB无法读取;

  2. DOM无法获得;

  3. Ajax请求不能发送。

下来说说如何针对上面三种情况进行跨域。


Cookie

Cookie是服务器写入浏览器的一小段信息,只有同源的网页才能共享,但是,两个页面一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享Cookie。

举例来说,A网页是http://hr.163.com/a.html,

B网页是http://study.163.com/b.html。

主要设置相同的document.domain,两个网页就可以共享Cookie。

我们在两个页面都设置:

1

document.domain ='163.com';

然后在a网页设置cookie:

1

document.cookie ="test1=hello";

就可以在b网页读到这个cookie了:

1

varallCookie =document.cookie;

我们也可以在服务器进行配置,指定Cookie的所属域名为一级域名,这样,二级三级域名不用做任何设置,都可以读取这个cookie:

1

Set-Cookie: key=value; domain=.163.com; path=/


iframe

如果两个网页不同源,就无法拿到对方的DOM,典型的例子就是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

父窗口获取子窗口:

1

document.getElementById("myIFrame").contentWindow.document

子窗口获取父窗口:

1

window.parent.document.body

如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain即可。

如果是完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。

  1. 片段标示符

  2. window.name

  3. 跨文档通信API

片段标示符

片段标示符是URL的#后面的部分,比如

http://163.com/a.html#fragment的#fragment,如果只是改变片段标示符,页面不会重新刷新。

父窗口可以把信息,写入子窗口的片段标示符:

1

2

varsrc = originURL +'#'+ data;

document.getElementById('myIFrame').src = src;

子窗口可以通过监听hashchange事件得到通知:

1

2

3

4

5

6

window.onhashchange = checkMessage;

functioncheckMessage(){

varmessage =window.location.hash;

// ...

}

同样的,子窗口也可以改变父窗口的片段标示符:

1

parent.location.href= target +"#"+ hash;

window.name

浏览器窗口有window.name属性,这个属性最大的特点是,无论是否同源,只要在同一个窗口里,前一个页面设置了这个属性,后一个网页可以读取它。

父窗口先打开一个子窗口,载入一个不同源的页面,该网页将信息写入window.name属性:

1

window.name = data;

子窗口跳回一个与主窗口同域的网址:

1

location ='http://parent.url.com/xx.html';

然后,主窗口就可以读取子窗口的window.name了:

1

vardata =document.getElementById('myFrame').contentWindow.name;

优点是window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性值的变化,影响页面性能。

window.postMessage

HTML5的全新API:跨文档通信API。

父窗口

http://aaa.com

向子窗口http://bbb.com

发消息,调用postMessage方法就可以了:

1

2

varpopup =window.open('http://bbb.com','title');

popup.postMessage('Hello World!','http://bbb.com');

posetMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即”协议 + 域名 + 端口”。也可以设为*,表示不限制域名,向所有窗口发送。

子窗口向父窗口发送消息的写法类似:

1

window.opener.postMessage('Nice to see you','http://aaa.com');

父窗口和子窗口都可以通过message事件,监听对方的消息:

1

2

3

window.addEventListener('message',function(e){

console.log(e.data);

},false);

message事件的事件对象event,提供以下三个属性:

  1. event.source:发送信息的窗口

  2. event.origin:信息发向的网址

  3. event.data:信息内容

下面可以看看几个例子:

子窗口通过event.source属性引用父窗口,然后发送信息:

1

2

3

4

window.addEventListener('message', receiveMessage);

functionreceiveMessage(event){

event.source.postMessage('Nice to see you!','*');

}

event.origin属性可以过滤不是发送本窗口的信息:

1

2

3

4

5

6

7

8

9

window.addEventListener('message', receiveMessage);

functionreceiveMessage(event){

if(event.origin !=='http://aaa.com')return;

if(event.data ==='Hello World') {

event.source.postMessage('Hello', event.origin);

}else{

console.log(event.data);

}

}


AJAX

ajax的跨域需求很常见的。现在有三种方法来解决:

  1. JSONP

  2. WebSocket

  3. CORS

JSONP

JSONP是服务器与客户端跨域通信的常用方法,最大的特点是简单适用,老式浏览器全部支持,服务器改造非常小。

它的思想是,页面通过添加一个<script>元素,向服务器请求JSON数据,服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入<script>元素,由它向跨源网址发出请求:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

functionaddScriptTag(src){

varscript =document.createElement('script');

script.setAttribute("type","text/javascript");

script.src = src;

document.body.appendChild(script);

}

window.onload =function(){

addScriptTag('http://example.com/ip?callback=foo');

}

functionfoo(data){

console.log('Your public IP address is: '+ data.ip);

};

服务器收到这个请求后,会将数据放在回调函数的参数位置返回:

1

2

3

foo({

"ip": "8.8.8.8"

});

JSONP只能发GET请求,这点需要注意。

WebSocket

WebSocket是一种通信协议,使用ws://(非加密)

和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨域通信。

下面举例子,浏览器发出的websocket请求头:

1

2

3

4

5

6

7

8

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==

Sec-WebSocket-Protocol: chat, superchat

Sec-WebSocket-Version: 13

Origin: http://example.com

其中Origin字段,表示该请求的请求源,服务器可以判断这个字段,判断是否允许本次通信,如果同意,做出反应:

1

2

3

4

5

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

Sec-WebSocket-Protocol: chat

CORS

CORS可以允许任何类型的请求,是W3C的新标准。

首先,在请求头信息中添加Origin字段:

1

2

3

4

5

6

GET /cors HTTP/1.1

Origin: http://api.bob.com

Host: api.alice.com

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.0...

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段:

1

2

3

4

Access-Control-Allow-Origin: http://api.bob.com

Access-Control-Allow-Credentials: true

Access-Control-Expose-Headers: FooBar

Content-Type: text/html; charset=utf-8

如果不在允许范围内,也会返回正常的HTTP回应,但是没有Access-Control-Allow-Origin字段,浏览器发现后,就知道出错了,从而抛出错误,被XMLHttpRequest的onerror回调函数捕获。


  上一篇:go jQuery面向对象的写法
  下一篇:go jQuery常用实用代码


评论


用QQ登录管理/创建网站 用微博登录管理/创建网站   发布于:11-19