关于前端的跨域问题一直都想去了解应该如何去解决,但由于没有实际例子,看了网上的很多例子一直都处于懵懂的状态,正好公司最近出了一个跨域的问题就随便学习总结了一下
配置域名
本文跨域处理后台以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的调用
局限性
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>