实现跨域访问的方法总结
解决跨域问题的常用方法
跨域, 是指调用方和被调用方的协议、域名和端口,不完全相同(即不同源),跨域的访问或调用会被浏览器禁止,因而跨域是浏览器端特有的,服务端无跨域问题。
服务端代理
服务端代理,又称为请求转发/反向代理
原理:
A站的页面跨域访问B站的服务端接口,A站请求自己的后端接口,A站的后端接口负责转发请求到B站,并将B站的响应返回给A站页面。
适用场景:
调用第三方服务接口
优点:
比较通用,可解决大多数跨域问题
**缺点: **
需要后端支持(提供转发接口),增加本站服务器负担,无法维持session状态
示例
系统A(A.com)对外提供一个邮件发送接口,可接收GET或POST方式传递参数(如email, subject, body等)数据。当系统b(b.com)需要使用到这个接口的功能时,并提供比较好的用户体验时,采取了通过ajax将用户填入的信息传递数据给这个接口,来实现发送邮件的目的。
系统b的页面请求自身的后端接口proxy.php
$.post(
'http://b.com/proxy.php',
{
email:"impng@impng.com",
subject:"ajax跨域之代理",
body:"这里是邮件内容"
},
function(data){
if(data == 1) alert('发送成功!');
}
);
proxy.php转发请求到系统A
$post = $_POST;
$data = array(
'email' => $post['email'],
'subject' => $post['subject'],
'body' => $post['body'],
);
$url = 'http://A.com/ISendMail.php?'; // 系统A的发送邮件接口
$url .= http_build_query($data);
$res = file_get_contents($url);
echo $res;
JSONP
原理:
由于引用外部资源不受同源策略限制,如 <img>
<link>
<script>
, 所以可通过动态插入<script>
标签, 访问跨域的服务接口,只要跨域接口把数据组装成函数调用的形式,前端页面就可以获取到数据.
优点:
完全由前端控制,不需要后端支持,兼容性好。
缺点:
只能get方式(因为本质是加载js)
示例:
<!doctype html>
<html>
<head>
<script>
function loadContent() {
var s = document.createElement('SCRIPT');
s.src = 'http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent'; //指定回调
document.body.appendChild(s);
}
function setDivContent(v) {
var dv = document.getElementById("dv");
dv.innerHTML = v;
}
</script>
</head>
<body>
<div id="dv"></div>
<input value="Click Me">
</body>
</html>
// 其中的www.anotherdomain.com/TestCrossJS.aspx是这样的,
<script runat="server">
void Page_Load(object sender, EventArgs e) {
string f = Request.QueryString["f"];
Response.Clear();
Response.ContentType = "application/x-javascript";
Response.Write(String.Format(@" {0}('{1}'); ", f, DateTime.Now)); //组装函数调用表达式
Response.End();
}
</script>
iframe监听hash
在页面内嵌或动态生成指向别的网站的IFRAME,然后这2个网页间可以通过改变对方的anchor hash fragment来传输消息。改变一个网页的anchor hash fragment并不会使浏览器重新加载网页,所以一个网页的状态得以保持,而网页本身则可以通过一个计时器(timer)来察觉自己anchor hash的变化(h5 window.onhashchange),从而获取数据并作出响应。
父页面
<!doctype html>
<html>
<head>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Test iframe cross domain</title>
<script>
var url = "http://localhost:8081/cross_sub.html";
var oldHash = null;
var timer = null;
function getHash() {
var hash = window.location.hash;
if ((hash.length >= 1) && (hash.charAt(0) == '#')) {
hash = hash.substring(1); //获得#后的值
}
return hash;
}
function sendRequest() {
var d = document; //简写document
var t = d.getElementById('request');
var f = d.getElementById('ifrm');
f.src = url + "#" + t.value + (new Date().getTime());//修改iframe.src的hash
}
function setDivHtml(v) {
var d = document;
var dv = d.getElementById('response');
dv.innerHTML = v;
}
function idle() {
var newHash = getHash();
if (newHash != oldHash) {
setDivHtml(newHash);
oldHash = newHash;
}
timer = window.setTimeout(idle, 100); // 递归实现循环 定时检查自身hash的变化
}
window.onload = function (){
timer = window.setTimeout(idle, 100);
};
window.name = 'parent';
</script>
</head>
<!-- localhost:8080/cross_parent.html -->
<body>
<h1>THIS IS THE PARENT PAGE</h1>
<input type="text" id="request" />
<input type="submit" value="send" id="send" />
<div id="response" style="margin: 30px; padding: 30px; border:1px dashed #ccc;">
</div>
<iframe src="http://localhost:8081/cross_sub.html" frameborder="0" id="ifrm" style="border:5px solid #ddd; padding:20px; width:80%; margin:auto; display:block; height:300px; "></iframe>
<script type="text/javascript">
var sendBtn = document.getElementById('send');
sendBtn.onclick = sendRequest;
</script>
</body>
</html>
子页面
<!doctype html>
<html>
<head>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Test iframe cross domain</title>
<script>
var url = "http://localhost:8080/cross_parent.html";
var oldHash = null;
var timer = null;
function getHash() {
var hash = window.location.hash;
if ((hash.length >= 1) && (hash.charAt(0) == '#')) {
hash = hash.substring(1);
}
return hash;
}
function sendRequest() {
var d = document;
var t = d.getElementById('request');
var f = parent;
//alert(f.document); //试着去掉这个注释,你会得到“Access is denied”
f.location.href = url + "#" + t.value +( new Date().getTime() ); //修改父窗口的hash
}
function setDivHtml(v) {
var d = document;
var dv = d.getElementById('response');
dv.innerHTML = v;
}
function idle() {
var newHash = getHash();
if (newHash != oldHash) {
setDivHtml(newHash);
oldHash = newHash;
}
timer = window.setTimeout(idle, 100);
}
window.onload = function() {
timer = window.setTimeout(idle, 100);
};
window.name = 'sub';
</script>
</head>
<!-- localhost:8081/cross_sub.html -->
<body>
<h1>THIS IS THE SUB PAGE</h1>
<input type="text" id="request" />
<input type="submit" value="send" id="send" />
<div id="response" style="margin: 30px; padding: 30px; border:1px dashed #ccc;">
</div>
<script type="text/javascript">
var sendBtn = document.getElementById('send');
sendBtn.onclick = sendRequest;
</script>
</body>
</html>
window.name
window 对象的name属性是一个很特别的属性,当该window的location变化,然后重新加载,它的name属性可以依然保持不变。那么我们可以在页面 A中用iframe加载其他域的页面B,而页面B中用JavaScript把需要传递的数据赋值给window.name,iframe加载完成之后,页面A修改iframe的地址,将其变成同域的一个地址,然后就可以读出window.name的值了。这个方式非常适合单向的数据请求,而且协议简单、安全。不会像JSONP那样不做限制地执行外部脚本。
父页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>window.name test</title>
</head>
<body>
<button onclick="toggleIfrmSrc()">切换iframe src</button>
<iframe id="ifrm" src="http://localhost:9200/a.html" frameborder="0" style="width: 500px; height: 500px;"></iframe>
<script>
var $ = function(s ) {
return document.querySelector(s);
}
function toggleIfrmSrc() {
if (!oldUrl) {
oldUrl = ifrm.src;
}
var isCross = /:9200/.test(ifrm.src);
var newUrl = isCross ? '/a-same.html' : oldUrl;
ifrm.src = newUrl;
console.log('subwin.name:', subwin.name); // 获取到
}
var oldUrl = '';
var ifrm = $('#ifrm');
var subwin = ifrm.contentWindow;
console.log('subwin.name:', subwin.name); // 跨域,获取不到
</script>
</body>
</html>
子页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>window.name test </title>
</head>
<body>
<h2>sub page, window.name</h2>
<script>
window.name = 'iamA';
var c = 1;
setInterval(function () {
window.name += c;
}, 2000);
</script>
</body>
</html>
document.domain
仅仅是子域名不同,可改为同域。如:blog.exam.com, exam.com blog.examp.com/hello.html中,可设置 document.domain = 'exam.com';
window.postMessage
h5 api
CROS
需后端支持