一:同源策略
同源是指,域名,协议,端口相同。
同源策略只允许使用与请求页面相同的主机、协议和端口进行通信,也就是不同域(example.com 和 example2.com)、不同主机(my.example.com 和 www.example.com)、不同协议(http://example.com 和 https://example.com)之间的通信是禁止的。
二:跨域请求
跨域访问有四种方式:
1)jsonp跨域
2)修改document.domain来跨子域
3)window.name跨域
4)使用HTML5中新引进的window.postMessage方法来跨域传送数据
三:JSONP跨域
AJAX 可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
AJAX面临两个问题:第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?目前,最推崇的首选方案是:用JSON来传数据,靠JSONP来跨域。
JSON(JavaScript Object Notation)和JSONP(JSON with Padding)虽然只有一个字母的差别,但他们根本不是一回事:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。一个是描述信息的格式,一个是信息传递双方约定的方法。
JSON是一种基于文本的数据交换方式,或者叫做数据描述格式。
JSONP(JSON with Padding)是JSON的一种“使用模式”,可解决浏览器的跨域数据访问的问题。
由于同源策略,一般位于 server1.example.com 的网页无法与不是 server1.example.com的服务器进行沟通,而 HTML 的<script> 元素是一个例外。利用 <script> 元素,网页可以得到从其他来源动态产生的 JSON 数据,这种使用模式就是JSONP。
用JSONP 抓到的数据并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。
JSONP用于跨域访问,因为ajax是不能进行跨域访问的。
JSONP类似于WebService。
Jsonp是利用script进行跨域访问的,如<script type = ”text/javascript” scr=”跨域地址”></script>,script标签本身就是用来执行javascript代码的,所以请求返回的响应必须是一段javascript代码。 在js中,直接用XMLHttpRequest请求不同域上的数据时,是不可以的,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
1.JSON格式即规则
1、JSON只有两种数据类型描述符,大括号{}和方括号[]。
英文冒号:是映射符,英文逗号,是分隔符,英文双引号""是定义符。
2、大括号{}用来描述一组“不同类型的无序键值对集合”,方括号[]用来描述一组“相同类型的有序数据集合”。
3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。
4、键值对以英文冒号:进行分隔。
5、JSON内部常用数据类型是字符串、数字、布尔、日期、null ,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,建议无特殊日期排需求的话,就将日期时间直接作为字符串传递。
2.JSONP跨域请求
前端页面代码:
方法一:
<script type="text/javascript">
function dosomething①(data){
alert(data.name+","+data.age);
}
</script>
<script type="text/javascript" src = "http://10.21.1.211:8080/jsonp_server/JsonpServlet?id=1&jsonpCallback=dosomething②">
</script>
解析:②所在的<script>用于访问不同域中的json数据,src是访问地址,?问号之后的callback是回调函数,②dothing是回调函数名,这个回调函数的名称可以随便,但是需要和①处的函数名称dothing相同,当访问成功后,执行函数名为①dothing的回调函数。Id=1是传递的参数。
JSONP原理:通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并会把我们需要的json数据作为参数传入返回。
方法二:
<script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript">
$(function(){
$.getJSON("http://10.21.1.211:8080/jsonp_server/JsonpServlet?jsonpCallback=?",{id:1},function(data){
alert(data.name+","+data.age);
});
})
</script>
解析:原理是一样的,只是不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数,{id:1}为传递的参数。
方法三:
<script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript">
$(function(){
$.ajax({
type: "GET",
//不同于当前域的一个URL地址
url: "http://10.21.1.211:8080/jsonp_server/JsonpServlet",
//传递的参数,jsonpCallback=?会产生一个随机数,这个随机数在客户端和服务器端进行传递,表示是同一个请求
data: "id=1&jsonpCallback=?",
//服务器端返回的数据类型
dataType: "jsonp",
// 指定回调函数,这个名称可以随便命名,但是需要和传递参数中的jsonpCallback=?相同
jsonp: 'jsonpCallback',
// 请求成功时执行
success: function (data) {
alert(data.name+","+data.age);
},
// 请求失败时执行
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求数据异常,状态码:" + XMLHttpRequest.status);
}
});
})
</script>
后端响应代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("客户端请求访问!");
Map<Integer, Object> map=new HashMap<Integer,Object>();
map.put(1, new Student("张三", 23));
map.put(2, new Student("李四", 24));
map.put(3, new Student("王五",25));
Integer id=Integer.parseInt(request.getParameter("id"));
Student student=(Student) map.get(id);
String json = "{\"name\":\""+student.getName()+"\",\"age\":"+student.getAge()+"}";
String jsonp = request.getParameter("jsonpCallback")+"("+json+")";
response.setContentType("text/plain;charset=utf-8");
response.getWriter().write(jsonp);
}
四:修改document.domain来跨子域
由于浏览器有同源策略,不同域的框架之间是不能进行js交互操作的。
虽然不同的框架之间(父子或同辈),是能够获取到彼此的window 对象的,但是却不能获取到window对象的属性和方法(html5中的postMessage方法是一个例外,某些浏览器也可以使用top、parent等少数几个属性)。
比如,有一个页面,它的地址是http://www.example.com/a.html,在这个页面里面有一个iframe,它的src是http://example.com/b.html, 这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。
http://www.example.com/a.html页面如下:
<script type="text/javascript">
function onLoad(){
var iframe=document.getElementById('iframe');
var win=iframe.contentWindow();//能获得iframe中的Window对象
var doc=win.document;//但不能获得document对象
var name=win.name;//也不能获得Window对象的名称
}
</script>
<iframe id="iframe" src="http://example.com/b.html" οnlοad="javascript:onLoad();">
</iframe>
这时,document.domain就派上用场了,只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但是,document.domain的设置是有限制的,只能把 document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。
在页面 http://www.example.com/a.html 中设置document.domain:
<script type="text/javascript">
document.domain="example.com";
function onLoad(){
var iframe=document.getElementById('iframe');
var win=iframe.contentWindow();//能获得iframe中的Window对象
var doc=win.document;//能获得document对象
var name=win.name;//也能获得Window对象的名称
}
</script>
<iframe id="iframe" src="http://example.com/b.html" οnlοad="javascript:onLoad();">
</iframe>
在页面 http://example.com/b.html 中也设置document.domain,而且是必须的,尽管b.html页面的domain是example.com,但是还是必须显示的设置。
<script type="text/javascript">
document.domain="example.com";
</script>
这样就可以通过js访问到iframe中的各种属性和对象了。
修改document.domain的方法只适用于不同子域的框架间的交互。
五:window.name跨域
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个 window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中[window.name为最近设置的值],并不会因新页面的载入而进行重置。
比如:页面a.html的代码如下:
<script type="text/javascript">
window.name="跨域请求测试";
setTimeout(function(){
window.location="b.html";
}, 3000);
</script>
页面b.html的代码如下:
<script type="text/javascript">
alert(window.name);
</script>
页面a.html载入后3秒会自动加载b.html页面,会弹出在a.html页面中设置的window.name,即在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面中都没对 window.name进行修改,那么所有页面获取到的window.name的值都是a.html页面设置的值。如果有需要,可以在载入的任何一个页面对window.name的值进行修改。注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器。
六:使用HTML5新引进的window.postMessage方法来跨域
window.postMessage(message,targetOrigin)方法是html5新引进的特性,可以向其它的window对象发送消息,无论这个window对象是属于同源或不同源。
调用postMessage方法的window对象是指要发送消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 *。
上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的情况,每一个框架都有一个window对象。
比如:有两个页面
http://www.example.com/a.html页面代码如下:
<script type="text/javascript">
function onLoad(){
var iframe=document.getElementById('iframe');
var win=iframe.contentWindow;//获取window对象
win.postMessage('来自页面a.html的消息','*');//向不同域的页面发送消息
}
</script>
<iframe id="iframe" src="http://www.example.com/b.html" οnlοad="javascript:onLoad();">
</iframe>
http://www.example.com/b.html页面代码如下:
<script type="text/javascript">
window.onmessage=function(e){//注册message事件用来接收消息
e=e||event;//获取事件对象
alert(e.data);//通过data属性得到传送的消息
}
</script>
七:总结
1)jsonp跨域 ---<script>标签引入
2)修改document.domain来跨子域---针对<iframe>框架,获取子域框架中的属性和方法
3)window.name跨域---在一个窗口(window)的生命周期内,窗口载入的所有的页面都共享一个 window.name,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中,window.name的值是最近一次设置的值。
4)使用HTML5中新引进的window.postMessage方法来跨域---针对<iframe>框架,一个页面有多个框架的时候,每个框架都有一个window对象。调用postMessage方法的window对象是指要发送消息的window对象,有两个参数,第一个是message要发送的消息,字符串;第二个是targetOrigin限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 *。
请求方式 | 原理 | 示例代码 |
JSONP | JSONP用于跨域访问,ajax是不能进行跨域访问的。JSONP类似于变相的WebService,是利用script标签进行跨域访问,如<script type=”text/javascript” scr=”跨域地址”></script>,script标签本身就是用来执行javascript代码的,所以请求返回的响应必须是一段javascript代码。 | 方式一: <script> function dosomething(data){ alert(data.name+",”+data.age);} </script> <script src="http://10.21.1.211:8080/jsonp_server/JsonpServlet?id=1&jsonpCallback=dosomething"> </script> 方式二: <script type="text/javascript" src="js/jquery-1.8.3.min.js"></script> <script type="text/javascript"> $(function(){ $.getJSON("http://10.21.1.211:8080/jsonp_server/JsonpServlet?jsonpCallback=?",{id:1},function(data){alert(data.name+","+data.age); }); }) </script> 方式三: <script type="text/javascript" src="js/jquery-1.8.3.min.js"></script> <script type="text/javascript"> $(function(){ $.ajax({ type: "GET", url: "http://10.21.1.211:8080/jsonp_server/JsonpServlet", data: "id=1&jsonpCallback=?", dataType: "jsonp", jsonp: 'jsonpCallback', success: function (data) { alert(data.name+","+data.age); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert("请求数据异常,状态码:" + XMLHttpRequest.status); } }); }) </script> |
window.name | window对象有个name属性,其特征:在一个窗口(window)的生命周期内,窗口载入的所有的页面都共享一个 window.name,每个页面对window.name都有读写的权限。window.name的值是最近修改过的值。 通过window.name来传递数据。 | a.html的代码如下: <script> window.name="跨域请求测试"; setTimeout(function(){ window.location="b.html"; }, 3000); </script> b.html的代码如下: <script> alert(window.name); </script> |
document.domain | 获取页面嵌入的iframe框架中window对象的属性和方法 | 在本页面和iframe嵌入的页面中设置相同的document.domain |
window.postMessage | window.postMessage(message,targetOrigin)方法是html5新引进的特性,可以向其它的window对象发送消息,无论这个window对象是属于同源或不同源。 | <script> function onLoad(){ var iframe = document.getElementById('iframe'); var win = iframe.contentWindow;//获取window对象 win.postMessage('来自页面a.html的消息','*');//向不同域的页面发送消息 } </script> <iframe id="iframe" src = "http://www.example.com/b.html" onload = "javascript:onLoad();"> </iframe> <script type="text/javascript"> window.onmessage=function(e){//注册message事件用来接收消息 e=e||event;//获取事件对象 alert(e.data);//通过data属性得到传送的消息 } </script> |