浏览器跨域问题解决办法
来源:互联网 发布:ae cc 2017 mac 中文 编辑:程序博客网 时间:2024/05/20 09:08
前言
之前在一个项目中,调用了别的部门的接口,但是由于存在跨域问题,只能进行简单的本地数据模拟调试,然后再把资源给对方进行联调。这样的方法肯定不是最佳方案,正好最近看到一篇文章,说到的就是跨域问题的解决办法,所以进行了学习和整理。
同源政策
首先说说同源政策,最初,它的含义是指,A网页设置的Cookie,B网页不能打开,除非这两个网页“同源”。所谓“同源”,就是三个相同:
- 协议相同
- 域名相同
- 端口相同
同源政策的目的就是为了防止信息泄露。
如果非同源,共有三种行为会受到限制:
- Cookie、LocalStorage和IndexDB无法读取;
- DOM无法获得;
- Ajax请求不能发送。
下来说说如何针对上面三种情况进行跨域。
Cookie
Cookie是服务器写入浏览器的一小段信息,只有同源的网页才能共享,但是,两个页面一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享Cookie。
举例来说,A网页是http://hr.163.com/a.html,
B网页是http://study.163.com/b.html。
主要设置相同的document.domain,两个网页就可以共享Cookie。
我们在两个页面都设置:
document.domain = '163.com';
- 1
- 1
然后在a网页设置cookie:
document.cookie = "test1=hello";
- 1
- 1
就可以在b网页读到这个cookie了:
var allCookie = document.cookie;
- 1
- 1
我们也可以在服务器进行配置,指定Cookie的所属域名为一级域名,这样,二级三级域名不用做任何设置,都可以读取这个cookie:
Set-Cookie: key=value; domain=.163.com; path=/
- 1
- 1
iframe
如果两个网页不同源,就无法拿到对方的DOM,典型的例子就是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。
父窗口获取子窗口:
document.getElementById("myIFrame").contentWindow.document
- 1
- 1
子窗口获取父窗口:
window.parent.document.body
- 1
- 1
如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain即可。
如果是完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。
- 片段标示符
- window.name
- 跨文档通信API
片段标示符
片段标示符是URL的#后面的部分,比如
http://163.com/a.html#fragment的#fragment,如果只是改变片段标示符,页面不会重新刷新。
父窗口可以把信息,写入子窗口的片段标示符:
var src = originURL + '#' + data;document.getElementById('myIFrame').src = src;
- 1
- 2
- 1
- 2
子窗口可以通过监听hashchange事件得到通知:
window.onhashchange = checkMessage;function checkMessage() { var message = window.location.hash; // ...}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
同样的,子窗口也可以改变父窗口的片段标示符:
parent.location.href= target + "#" + hash;
- 1
- 1
window.name
浏览器窗口有window.name属性,这个属性最大的特点是,无论是否同源,只要在同一个窗口里,前一个页面设置了这个属性,后一个网页可以读取它。
父窗口先打开一个子窗口,载入一个不同源的页面,该网页将信息写入window.name属性:
window.name = data;
- 1
- 1
子窗口跳回一个与主窗口同域的网址:
location = 'http://parent.url.com/xx.html';
- 1
- 1
然后,主窗口就可以读取子窗口的window.name了:
var data = document.getElementById('myFrame').contentWindow.name;
- 1
- 1
优点是window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性值的变化,影响页面性能。
window.postMessage
HTML5的全新API:跨文档通信API。
父窗口
http://aaa.com
向子窗口http://bbb.com
发消息,调用postMessage方法就可以了:
var popup = window.open('http://bbb.com', 'title');popup.postMessage('Hello World!', 'http://bbb.com');
- 1
- 2
- 1
- 2
posetMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即”协议 + 域名 + 端口”。也可以设为*,表示不限制域名,向所有窗口发送。
子窗口向父窗口发送消息的写法类似:
window.opener.postMessage('Nice to see you', 'http://aaa.com');
- 1
- 1
父窗口和子窗口都可以通过message事件,监听对方的消息:
window.addEventListener('message', function(e) { console.log(e.data);},false);
- 1
- 2
- 3
- 1
- 2
- 3
message事件的事件对象event,提供以下三个属性:
- event.source:发送信息的窗口
- event.origin:信息发向的网址
- event.data:信息内容
下面可以看看几个例子:
子窗口通过event.source属性引用父窗口,然后发送信息:
window.addEventListener('message', receiveMessage);function receiveMessage(event) { event.source.postMessage('Nice to see you!', '*');}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
event.origin属性可以过滤不是发送本窗口的信息:
window.addEventListener('message', receiveMessage);function receiveMessage(event) { if (event.origin !== 'http://aaa.com') return; if (event.data === 'Hello World') { event.source.postMessage('Hello', event.origin); } else { console.log(event.data); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
AJAX
ajax的跨域需求很常见的。现在有三种方法来解决:
- JSONP
- WebSocket
- CORS
JSONP
JSONP是服务器与客户端跨域通信的常用方法,最大的特点是简单适用,老式浏览器全部支持,服务器改造非常小。
它的思想是,页面通过添加一个<script>
元素,向服务器请求JSON数据,服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
首先,网页动态插入<script>
元素,由它向跨源网址发出请求:
function addScriptTag(src) { var script = 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');}function foo(data) { console.log('Your public IP address is: ' + data.ip);};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
服务器收到这个请求后,会将数据放在回调函数的参数位置返回:
foo({ "ip": "8.8.8.8"});
- 1
- 2
- 3
- 1
- 2
- 3
JSONP**只能发GET请求**,这点需要注意。
WebSocket
WebSocket是一种通信协议,使用ws://(非加密)
和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨域通信。
下面举例子,浏览器发出的websocket请求头:
GET /chat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==Sec-WebSocket-Protocol: chat, superchatSec-WebSocket-Version: 13Origin: http://example.com
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
其中Origin字段,表示该请求的请求源,服务器可以判断这个字段,判断是否允许本次通信,如果同意,做出反应:
HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=Sec-WebSocket-Protocol: chat
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
CORS
CORS可以允许任何类型的请求,是W3C的新标准。
首先,在请求头信息中添加Origin字段:
GET /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段:
Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: FooBarContent-Type: text/html; charset=utf-8
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
如果不在允许范围内,也会返回正常的HTTP回应,但是**没有**Access-Control-Allow-Origin字段,浏览器发现后,就知道出错了,从而抛出错误,被XMLHttpRequest的onerror回调函数捕获。
根据详细的字段配置,可以看看这篇文章
,讲的非常详细。
- 浏览器跨域问题解决办法
- 浏览器跨域问题解决办法
- 跨域问题解决办法
- AJAX跨域问题解决办法
- css样式兼容不同浏览器问题解决办法
- IE浏览器的各种问题解决办法
- 浏览器网页打不开问题解决办法汇总
- web service跨域访问问题解决办法
- ajax请求ashx跨域问题解决办法
- ajax服务跨域问题解决办法
- 关于js的跨域问题解决办法
- nodejs浏览器跨域问题解决
- 浏览器跨域问题解决方案
- Silverlight跨线程问题解决办法
- ExtJs关于grid高度自适应浏览器的问题解决办法
- ExtJs关于grid高度自适应浏览器的问题解决办法
- MAC chrome浏览器地址栏无法搜索问题解决办法
- 图片在IE8浏览器多一个有边框问题解决办法
- springboot乐观锁
- ubuntu14编译安装llvm&clang 3.9
- OC中的 __attribute__ (人为警告⚠️ 黄色提示)
- Element type "Select" must be declared.
- php如何正则提取指定[]中的值
- 浏览器跨域问题解决办法
- Elasticsearch5.0 beta版本安装错误
- 【cmd】查看物理地址(查看mac地址)
- String 类
- classes can be instantiated explicitly or implicitly 明确和隐藏建立对象
- vue的ajax库 ----- vue-resource
- UICollectionView 基础全面解析
- 为什么还要读马克思?后续马克思博客转战博客园更新,周知
- React Native 的图片点击放大效果的组件使用 react-native-zoom-image