今天来总结下前端跨域问题的解决方案,网上有很多这个问题的总结和描述了;跨域问题也是前端的一个老问题了,现在就来简单聊聊。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,它要求请求的协议,端口,域名一致。如果请求的协议,端口,域名任何一个不一样,则出现跨域问题。同源策略的限制有2个:
- 不能通过ajax的方法去请求不同源中的文档
- 不同域的iframe之间不能进行js交互操作
前端跨域问题时常出现,目前有以下7中常用的跨域解决方案:
1、JSONP
大家耳熟能详的一个解决跨域问题的方式是使用JSONP,实现的原理是:<script>标签可以加载跨域的javascript脚本,并且被加载的脚本和当前文档属于同一个域。
JSONP方式由2部分组成:回调函数和数据。callback回调函数是接受response响应在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
同时服务器端需要进行相应的处理,即请求的callback回调函数,并将运算产生的数据为参数调用callback函数。
1. 前端向服务器发送请求,并带上callback回调函数
<script type="text/javascript">
function dosomething(jsondata){
//处理获得的json数据
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>
2. 后端接受请求获取callback
<?php
$callback = $_GET['callback'];//得到回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>
如果项目中使用的jquery,jquery会自动生成一个全局函数来替换callback=?中的问号
<script type="text/javascript">
$.getJSON('http://example.com/data.php?callback=?,function(jsondata)'){
//处理获得的json数据
});
</script>
$.getJSON方法会自动判断是否跨域,不跨域就调用普通的ajax方法;跨域则会以异步加载js文件的形式来调用jsonp的回调函数。
2、iframe+domain
通过设置document.domain共享cookie这种方式主要是用来跨子域。domain的设置有一定的限制,只能将domian设置成自身或更高一级的父域,且主域相同。
try {
requestByFrame();
} catch (e) {
document.domain = (location.host.match(/[^.]+\.[^.]+$/)[0]).replace(/:\d+/, "");
//跨域
var iframe = document.createElement("iframe");
iframe.id = iframe.name = "proxyFrame";
iframe.style.display = "none";
var proxyUrl = location.protocol + "//" + main.domain + "/proxy.html?rnd=" + Math.random();
iframe.src = proxyUrl;
document.body.appendChild(iframe);
iframe.onload = function() {
requestByFrame();
}
}
function requestByFrame() {
var win = frames["proxyFrame"];
if (win.location.host === top.location.host) {
throw "error";
} else {
conf.xhr = new win.XMLHttpRequest;
ajax(conf);
//...
}
}
3、postMessage
html5中新增了window.postMessage(message,targetOrigin) 方法,可以使用它来向其它的window对象发送消息。无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持。
postMessage(message,targetOrigin)接受2个参数,第一个参数message是是消息体,支持字符串和对象;第二个参数目标域名,可设置成*,表示所有的域都可以接受消息;
window.parent.postMessage({
act:'response',
msg:{
answer:url
}
},'a.cn');
在目标域a.cn中接收postMessage发送过来的消息体
//a.cn中监听第三方postMessage发送过来的消息
window.addEventListener('message', function(e){
if (e.data.act == 'response' && e.origin.indexOf("a.cn") > -1) {
var answer = e.data.msg.answer;
if(answer.indexOf("http://") > -1 || answer.indexOf("https://") > -1){
alert(e.data.msg.answer);
}
} else {
console.log('未定义的消息: '+ e.data.act);
}
}, false);
4、CORS
CORS(Cross-origin resource sharing)跨域资源共享适用于通过XMLHttpRequest / XDomainRequest请求接口的情形,其思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
实现CORS跨域,需要服务器进行配置,主要是三点:允许跨域的域名(Access-Control-Allow-Origin),允许请求的方法(Access-Control-Allow-Methods),允许请求的方式(Access-Control-Allow-Headers)。
CORS 和 JSONP的区别:
1.JSONP只支持GET方式,CORS支持所有的HTTP请求;
2.CORS可以像普通的XMLHttpRequest发起请求和获得数据;
3.JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS);
详细介绍可参看CORS详解
5、nginx反向代理
利用nginx反向代理来实现跨域请求是目前大部分公司采用的方式。
在nginx中增加location反向代理到服务器端设置:
server {
listen 80;
server_name serverName;
location /m/ {
proxy_pass http://example/xxx/api; //将/m/的请求转发example上
}
}
6、WebSocket
利用WebSocket可以进行跨域访问的原因是因为WebSocket使用ws://(非加密)和wss://(加密)作为协议,该协议不实行同源政策,所以可以进行跨域请求。
7、window.name
window对象有个name属性,在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,window.name大小2M左右。
实现:a.html跨域请求不同域中的data.html内的window.name数据,在a.html 新建一个隐藏的proxy的iframe ,iframe.src ='xxx/b.html',设置iframe的的onload方法,在onload function中获取b.html中的window.name值,再设置iframe的src为与a.html同源的b.html或者为about:blank。
例如:在index中,创建一个index2.设置对应的window.name.然后修改href到对应的iframe页面.处理完成后由iframe修改href返回到index域下的某个页面,读取window.name。
跨域总结完成,如有纰漏欢迎指正~~