← Back Home

跨域

  1. Ajax
  2. PHP

关于前端的跨域问题一直都想去了解应该如何去解决,但由于没有实际例子,看了网上的很多例子一直都处于懵懂的状态,正好公司最近出了一个跨域的问题就随便学习总结了一下

配置域名

本文跨域处理后台以PHP为例,首先下载wamp,我们先在本地配置俩个域名:pcd.me(一级域名)与dev.pcd.me(二级域名)。在www目录下新建pcd和dev俩个文件,当访问pcd.me时则访问pcd文件夹下的文件,访问dev.pcd.me则访问dev文件夹下的文件。

更改host

更改C:\Windows\System32\drivers\etc\host文件:添加

127.0.0.1 pcd.me
127.0.0.1 dev.pcd.me

更改httpd-vhosts.conf

更改F:\wamp2\wamp\bin\apache\apache2.4.23\conf\extra\httpd-vhosts.conf(该目录为我的配置,具体以实际为准)

<VirtualHost *:80>
	ServerName pcd.me
	DocumentRoot F:/wamp2/wamp/www/pcd
	<Directory  "F:/wamp2/wamp/www/pcd/">
		Options +Indexes +Includes +FollowSymLinks +MultiViews
		AllowOverride All
		Require local
	</Directory>
</VirtualHost>
<VirtualHost *:80>
	ServerName dev.pcd.me
	DocumentRoot F:/wamp2/wamp/www/dev
	<Directory  "F:/wamp2/wamp/www/dev/">
		Options +Indexes +Includes +FollowSymLinks +MultiViews
		AllowOverride All
		Require local
	</Directory>
</VirtualHost>

访问

在pcd文件下新建index.html

<!DOCTYPE html>
 <html lang="en">
 <head>
 	<meta charset="UTF-8">
 	<title>Document</title>
 </head>
 <body>
 	main
 </body>
 </html> 

在dev文件下新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Document</title>
</head>
<body>
   dev
</body>
</html>

此时访问pcd.me页面显示main,访问dev.pcd.me页面显示dev。

跨域请求

在pcd文件下ajax.php

<?php
	echo 1;
?>

在pcd.me发起异步请求即 pcd文件下的index文件更改为

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Document</title>
   <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
</head>
<body>
   dev
<script type="text/javascript">
$.ajax({
   type: "GET",
   url: "http://pcd.me/ajax.php"
   success: function(data) {
       console.log(data);//11
   }
})
</script>
</body>
</html>

在dev.pcd.me发起异步请求即 dev文件下的index文件更改为

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Document</title>
   <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
</head>
<body>
   dev
<script type="text/javascript">
$.ajax({
   type: "GET",
   url: "http://pcd.me/ajax.php"
   success: function(data) {
       console.log(data);//报错
       //XMLHttpRequest cannot load http://pcd.me/ajax.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://dev.pcd.me' is therefore not allowed access.
   }
})
</script>
</body>
</html>

当dev.pcd.me页面向pcd.me发起异步请求时则为跨域处理。至此环境搭建成功。

跨域处理之基本知识

浏览器厂商针对Web客户端制定实现重要的安全概念:同源策略(SOP)。它的核心是确保不同源提供的文件之间是相互独立的,只有当不同的脚本文件是有相同的域、端口、HTTP协议提供时,才没有特殊限制访问对方的DOM方法和属性。当一个脚本访问不同源的文档中的方法和属性使,便会抛出异常错误。 当我们需要访问不同源的文件时,要么绕开同源策略(JSNP与子域代理),要么使用跨域资源共享(CORS)的“正式”技术。

跨域处理之JSONP

实现原理

同源策略有个例外: HTML脚本是可以规避SOP检查的。JSONP利用这个例外实现跨域数据加载。

simple example

<script type="text/javascript" src="http://pcd.me/ajax.php"></script>

dev.pcd.me页面通过以上方式发起请求时,避开SOP,实现跨域请求,但对于返回的数据无法解析。 于是需要我们改写返回的数据形式

<?php
	$number = 11;
	echo 'abc('.$number.')';
?>
 <!DOCTYPE html>
 <html lang="en">
 <head>
 	<meta charset="UTF-8">
 	<title>Document</title>
 	<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
 </head>
 <body>
 	dev
 	<script type="text/javascript">
 	Window.abc = function(number) {
 		console.log(number);//11
 	}
 	</script>
	<script type="text/javascript" src="http://pcd.me/ajax.php"></script>
	
 </body>
 </html>

客户端通过定义一个函数,异步回调执行该函数,则函数中的参数为数据。

动态回调函数

window.jsonpCallback = function(json) {
	console.log(json)
}
var script = document.createElement('script');
script.async = true;
script.src = "http://pcd.me/ajax.php?callback=jsonpCallback";
document.body.appendChild(script);

<?php
	header('Content-type: application/javascript');
	$callback = $_GET['callback'];
	$person = json_encode(array(
			'name' => 'pcd',
			'age' => '21',
			'sex' => 'man'

		), JSON_PRETTY_PRINT);

	echo "$callback($person)";
?>

jquery中ajax的调用

jquery中ajax实现JSONP跨域处理

局限性

1.JSONP仅适用于HTTP的GET请求,譬如图片、文字等无法实现上传 2.JSONP返回调用回调函数,如何没有调用成功,则无任何提示,只能是给定一个时间,没有收到响应则为请求失败

子域名代理

浏览器根据SOP认为dev.pcd.me与pcd.me是不同的源,但浏览器允许网站将主机部分更改为原始值的后缀,即寄放在dev.pcd.me的页面可以将他的源设置为pcd.me,但不能修改源的端口号与HTTP协议。

ajax.php

<?php
	echo 11;
?>

dev.pcd.me下的请求


<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>子页面</title>
    <script type="text/javascript">document.domain = 'pcd.me'</script>
    <script type="text/javascript" src="jquery-1.11.2.min.js"></script>
</head>
<body>
	<div>dev</div>
	<script type="text/javascript">
    function getProducData() {
        var iframe = document.createElement('iframe');
        iframe.src = 'http://pcd.me/proxy.html';
        iframe.onload = function() {
            iframe.contentWindow.jQuery.ajax({//这里使用jquery的ajax方式调用则需要在proxy.html中引入jquery
                method: "GET",
                url: "http://pcd.me/ajax.php",
                success: function(data) {
                    console.log(data);
                }
            })
        }

        document.getElementsByTagName('head')[0].appendChild(iframe);
    }
    getProducData();
	</script>
</body>
</html>

pcd文件下添加一个代理文件proxy.html文件

<!DOCTYPE html>
<html>
	<script>
		document.domain = 'pcd.me';
	</script>
	<script type="text/javascript" src="//cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
</html>

局限性

1.只适用于子域名下,运用范围窄 2.需要添加一个代理文件

使用子域名代理及JSOP

ajax.php

<?php
	echo '<!DOCTYPE>
			<html>
				<script>
					document.domain = "pcd.me";
					window.parent.jsonpCallback("{\"status\": \"success\"}");
				</script>
			</html>
	';
?>

dev.pcd.me下的请求

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>子页面</title>
    <script type="text/javascript">document.domain = 'pcd.me'</script>
    <script type="text/javascript" src="jquery-1.11.2.min.js"></script>
</head>
<body>
	<div>dev</div>
    <script type="text/javascript">
    function jsonpCallback(value) {
        console.log(value);
    }
    </script>
	<script type="text/javascript">

    var frame = document.createElement('iframe');
    frame.name = "post-review";
    frame.style.display = "none";

    var form = document.createElement("form");
    form.action = "http://pcd.me/ajax.php";
    form.method = "GET";
    form.target = "post-review";
    document.body.appendChild(form);
    document.body.appendChild(frame);

    form.submit();

    document.body.removeChild(form);
	</script>
</body>
</html>

局限性

1.JSONP仅适用于HTTP的GET请求,譬如图片、文字等无法实现上传 2.JSONP返回调用回调函数,如何没有调用成功,则无任何提示,只能是给定一个时间,没有收到响应则为请求失败 3.相较于JSONP,其能实现POST请求

跨资源共享(CORS)

当发起http请求时,支持CORS的浏览器会通过引入额外的Origin头信息来指定请求源。 Origin: http://de.pcd.me

服务端的工作是检查头信息是否接受该请求,如果一个请求被接受,他必须发挥一个包含Access-Control-Allow-Origin: http://dev.pcd.me

<?php
	header('Access-Control-Allow-Origin: http://dev.pcd.me');
	echo 11;
?>

dev.pcd.me下的请求

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>子页面</title>
    <script type="text/javascript" src="jquery-1.11.2.min.js"></script>
</head>
<body>
	<div>dev</div>
    <script type="text/javascript">
    $.ajax({
        type: "GET",
        url: "http://pcd.me/ajax.php",
        success: function(data) {
            console.log(data);
        }
    })    
	</script>
</body>
</html>

使用postMessage实现iframe之间的通信

dev目录下index.html

 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <title>Test Cross-domain communication using HTML5</title> 
 <script type="text/JavaScript"> 
     function sendIt(){ 
         // 通过 postMessage 向子窗口发送数据
         document.getElementById("otherPage").contentWindow 
             .postMessage( 
                 document.getElementById("message").value, 
                "http://pcd.me"
             ); 
     } 
 </script> 
 </head> 
 <body> 
     <!-- 通过 iframe 嵌入子页面 --> 
     <iframe src="//pcd.me/other-domain.html" 
                 id="otherPage"></iframe> 
     <br/><br/> 
     <input type="text" id="message"><input type="button" 
             value="Send to child.com" onclick="sendIt()" /> 
 </body> 
 </html>

pcd目录下other-domain.html

<html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <title>Web page from dev.pcd.me</title> 
 <script type="text/JavaScript"> 
	 //event 参数中有 data 属性,就是父窗口发送过来的数据
	 window.addEventListener("message", function( event ) { 
		 // 把父窗口发送过来的数据显示在子窗口中
	   document.getElementById("content").innerHTML+=event.data+"<br/>"; 
	 }, false ); 

 </script> 
 </head> 
 <body> 
	 Web page from http://dev.pcd.me
	 <div id="content"></div> 
 </body> 
 </html>
comments powered by Disqus