跨域问题详解——九种解决跨域方法

跨域是前端再常见不过的问题了,下面主要针对跨域做一次总结,一次理清楚。

一、jsonp解决跨域

jsonp解决跨域问题的原理是:script不受同源策略的影响。
//前端代码:
<!DOCTYPE html>
<html lang="cn">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <script>
    function callback(res) {
      console.log(res)
    }
  </script>
  <script src="http://127.0.0.1:3000?fn=callback"></script>
  
</body>
</html>
//服务器代码
var http = require('http');
var server = http.createServer();
var qs = require("querystring")

server.on("request", (req, res) => {
  let params = qs.parse(req.url.split("?")[1])
  let obj = {name:"张三", age :20}
  res.end(params.fn + "(" + JSON.stringify(obj) +")")
})

server.listen(3000, () => {
  console.log("服务器已打开")
})
jsonp实现跨域:前端通过script标签将函数以参数的形式传递给后台,后台通过解
析请求的字符串,拿到该函数,然后让该函数以执行的状态返回给前端,并且在其中
传入数据。
不过jsonp请求只能支持get请求方式。

二、document.domain + iframe实现跨域

此方案只适用于主域相同,子域不同的页面实现跨域。
父窗口的地址为 http://www.fordmc.com/a.html
<body>
	<iframe src="http://ioc.fordmc.com/b.html"></iframe>
	<script>
		document.domain = "fordmc.com"  
		var name = "哈哈哈"
	</script>
</body>
子窗口的地址是 http://ioc.fordmc.com/b.html
<body>
	<script>
		document.domain = "fordmc.com"
		console.log(window.parent.name)   //哈哈哈
	</script>
</body>
总结:document.domain + iframe实现跨域原理是,当子域名不一致的情况下,
需要在父级和子级都需要使用document.domain强制设置基础主域名。然后在父级
中使用iframe将子集引入,这样在子集就能拿到父级的数据了。

三、location.hash + iframe实现跨域

存在两个页面(a, b),如果a,b页面同域,则可以直接进行数据通信。如果a, b不同
域时,可以通过location.hash + iframe来实现跨域数据传输。
a页面所在的域为:http://www.domain1.com/a.html
a页面的代码为:
<body>
	<iframe src="http://www.domain2.com/b.html"></iframe>
	<script>
		let iframe = document.querySelect("iframe")
		setTimeout(() => {
			iframe.src = iframe.src + "#user=admin"
		})
	</script>
</body>
<body>
	<script>
		window.onhashchange = function(){
			console.log(location.hash)  //#user=admin
		}	
	</script>
</body>
总结:上面两个页面处于不同的域中,在a页面中使用iframe引入了b页面,通过改
变b页面的hash值,来实现将数据传递给b页面的目的。同样b页面使用
onhashchange监听函数,监听hash值的改变。本例中hash值为user=admin,这样
就能拿到这个字符串,然后最终在b页面中解析,最终实现数据跨域传输。

四、window.name + iframe实现跨域

window.name存在这样一个特点就是在不同的页面下,或者是在不同的域下面其值都
是存在的,并且name的长度非常长(2MB)
a页面的地址是:http://www.domain1.com/a.html
<body>
	<script>
      var proxy = function (url, callback) {
      var state = 0;
      var iframe = document.createElement('iframe');

      // 加载跨域页面
      iframe.src = url;
      // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
      iframe.onload = function () {
        if (state === 1) {
          // 第2次onload(同域proxy页)成功后,读取同域window.name中数据
          callback(iframe.contentWindow.name);
          destoryFrame();

        } else if (state === 0) {
          // 第1次onload(跨域页)成功后,切换到同域代理页面
          iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';
          state = 1;
        }
      };

      document.body.appendChild(iframe);

      // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
      function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
      }
    };

    // 请求跨域b页面数据
    proxy('http://www.domain2.com/b.html', function (data) {
      console.log(data)
    });
	</script>
</body>
b页面的地址为:http://www.domain1.com/b.html
中间代理页,与a.html同域,内容为空即可。
c页面的地址是:http://www.domain2.com/c.html

<script>
	window.name = "张三"
</script>
总结:window.name + iframe实现的思路:本例中需要在a页面的中访问到c页面
的数据,但是由于跨域,无法进行访问。此时设置一个代理b页面,起到桥梁的作
用。首先在a页面中设置iframe并且首先将其的src值设置为页面c的地址。由于
iframe需要执行两次onload,此时我们可以在iframe第一次加载之后,改变其src
为页面b的地址。这样iframe上的window.name了,当改变src时,window.name也
不会改变。这样a和b就是同源的了,访问数据就不受限制了。最后需要销毁iframe。

五、postMessage实现跨域

postMessage是html5中新增的api,是window属性中位数不多的可以实现跨域的。
其存在两个参数,一个是发送的数据(使用JSON.stringify()来格式化一下)
第二个参数是:origin,表示为主机 + 协议 + 端口,"*"表示向全部的端口发送,
"/"表示向当前窗口同源的窗口发送。
a页面的地址为:http://www.domain1.com/a.html

<body>
	<iframe src="http://www.domain2.com/b.html">
	<script>
		let iframe = document.querySelector("iframe")
		iframe.onload = function(){
			let obj = { name:"张三",age:20 }
			
				iframe.contentWindow.postMessage(JSON.stringify(obj),"http://www.domain2.com")
				//向domain2发送数据
		}
		 //监听domain2发送来的数据
		window.addEventListener("message", function(data) {
			console.log(data)   //"data"
		})
	</script>
</body>
b页面的地址是:http://www.domain2.com/b.html
	<script>
		window.addEventListener("message", function(data){
			console.log(data)  //{name:"张三",age:20}
			window.parent.postMessage("data", "http://www.domain1.com")
		})	
	</script>

六、跨域资源共享(CORS)

目前跨域使用CORS比较多,如果不需要携带cookie,则只需要在服务端设置Access-control-Allow-Origin即可,如果需要携带cookie,则见下文。
前端设置
1、原生的ajax
// 前端设置是否带cookie
xhr.withCredentials = true;

2、实例代码
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    }
};
node后台进行设置
var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var postData = '';

    // 数据块接收中
    req.addListener('data', function(chunk) {
        postData += chunk;
    });

    // 数据接收完毕
    req.addListener('end', function() {
        postData = qs.parse(postData);

        // 跨域后台设置
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)
            /* 
             * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
             * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
             */
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');
  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值