原文地址:

1. 同源策略


        所有的浏览器都遵守同源策略,这个策略能够保证一个源的动态脚本不能读取或操作其他源的http响应和cookie,这就使浏览器隔离了来自不同源的内容,防止它们互相操作。所谓同源是指协议、域名和端口都一致的情况。举例来说,首先在Nginx上配置两个虚拟主机,一个监听80端口,另一个监听81端口:


[plain]  view plain copy print ?


1. server {  
2.         listen 80;  
3.         server_name localhost;  
4.           
5.         location / {  
6.             root   D:/dev/workspace;  
7.                     index  index.html index.htm;  
8.         }  
9.           
10.         location ~ \.php$ {  
11.             # 委托给后端的php  
12.             }  
13.     }


[php]  view plain copy print ?


1. // show_person.php  
2.   
3. <html>  
4.     <head>  
5. "text/javascript" src="js/jquery.min.js"></script>  
6. "text/javascript">  
7. function sayHello(person){  
8. ', your age is ' + person.age);  
9.             }  
10. // 通过jQuery的ajax请求person.php的数据  
11. 'http://localhost:80/MongoTest/person.php', function(data){  
12.                 sayHello(data);  
13.             });  
14.         </script>  
15.     </head>  
16.     <body>  
17.     </body>  
18. </html>




[php]  view plain copy print ?


1. // person.php  
2.   
3. <?php  
4. $person = array('name' => 'kobe', 'age' => 34);  
5. echo json_encode($person);  
6. ?>

然后先访问80端口试试,键入url:http://localhost/cross_domain/show_person.php。会弹出对话框,显示:kobe, your age is 34。


        然后再访问81端口,http://localhost:81/cross_domain/show_person.php,结果出错:


XMLHttpRequest cannot loadhttp://localhost/MongoTest/person.php. Origin http://localhost:81 is not allowed by Access-Control-Allow-Origin.


2. 打破同源策略的限制



        浏览器会阻止ajax请求非同源的内容,但没有限制<script>标签来访问非同源的内容,也没有限制使用动态添加<script>标签,所以可以采用这种方式打破这种限制。先测试下<script>访问非同源的内容,修改show_person.php,如下:



[php]  view plain copy print ?



1. // show_person.php  
2.   
3. <html>  
4.     <head>  
5. "text/javascript" src="js/jquery.min.js"></script>  
6. "text/javascript">  
7. function sayHello(person){  
8. ', your age is ' + person.age);  
9.             }  
10.         </script>  
11.           
12. "text/javascript" src="http://localhost:80/MongoTest/person.php" ></script>  
13.     </head>  
14.     <body>  
15.     </body>  
16. </html>



[php]  view plain copy print ?



    1. // person.php  
    2.   
    3. <?php  
    4. $person = array('name' => 'kobe', 'age' => 34);  
    5. echo 'sayHello('.json_encode($person).')';  
    6. ?>




            下面就看一下,动态添加<script>标签实现跨域请求数据。修改show_person.php:



    [php]  view plain copy print ?



      1. // show_person.php  
      2.   
      3. <html>  
      4.     <head>  
      5. "text/javascript" src="js/jquery.min.js"></script>  
      6. "text/javascript">  
      7. function sayHello(person){  
      8. ', your age is ' + person.age);  
      9.             }  
      10. // 通过callback参数指定回调函数  
      11. var url = 'http://localhost:80/MongoTest/person.php?callback=sayHello';  
      12. var script = document.createElement('script');  
      13. 'src', url);  
      14. 'head')[0].appendChild(script);   
      15.         </script>  
      16.     </head>  
      17.     <body>  
      18.     </body>  
      19. </html>



      [php]  view plain copy print ?


      1. // person.php  
      2.   
      3. <?php  
      4. $person = array('name' => 'kobe', 'age' => 34);  
      5. echo $_REQUEST['callback'].'('.json_encode($person).')';  
      6. ?>


              通过81端口访问,发现也会打印出结果。这就实现了跨域请求,可以通过firefox的firebug或者chrome的js控制台查看html元素发现,在head元素上多了一个<script>元素



      [html]  view plain copy print ?



      1. <script src="http://localhost:80/MongoTest/person.php?callback=sayHello"></script>



      3. JSONP



              实际上,上面的例子就是一个JSONP的简单实现。JSONP(JSON with Padding)就是服务器端和客户端互相协作以完成跨域请求的一种协议,客户端向服务器端发送请求并附带callback函数,服务器端返回相应的js代码,这个代码就是执行回调函数,参数就是服务器端返回的JSON数据。上面例子中的person.php就是服务端的简单实现,返回的响应内容是sayHello({"name":"kobe","age":34}),这段内容会被客户端插入到动态生成的script标签内部。



              客户端在发起跨域请求时,需要制定具体的回调函数,比如这个请求http://localhost:80/MongoTest/person.php?callback=sayHello,callback就是回调函数,服务器端也要通过callback要提取回调函数名,所以具体的这个参数需要客户端和服务器端达成一致,否则不能实现跨域。



              在jQuery中拥有对JSONP的支持,只要在使用jQuery.getJSON方法时传入的url的格式是url?callback=?即可,jQuery会自动的将?替换成具体的回调函数名。上面例子的功能,可以采用jQuery改写成:



      [javascript]  view plain copy print ?


      1. $.getJSON('http://localhost:80/MongoTest/person.php?callback=?', function(data){  
      2.     sayHello(data);  
      3. });


      jQuery17103600438670255244_1331202380099({"name":"kobe","age":34})