跨域解决方案(一)详细篇

一、前言

跨域问题在前端已经是老生常谈了,本文围绕着为什么产生跨域以及几种解决方案详细说明。

二、跨域起源

说到跨域不得不提到浏览器的同源策略(1995年,Netscape公司提出的,现在适用于所有浏览器),所谓同源策略,即协议相同、域名相同、端口相同。如果三者之一有所不同就认为是跨域。

三、跨域示例

域名:    https://www.hao123.com/index.html(默认80端口)

同源:    https://www.hao123.com/find.html(协议、域名、端口均相同)

不同源:http://www.hao123.com/index.html(https 和 http,协议不同)

              https://www.hao123456.com/index.html(hao123 和 hao123456.com,域名不同)

              https://www.hao123.com:8090/index.html(80 和 8090,端口不同)

四、跨域解决方案

1.document.domain

目标:父域A页面(https://www.hao123.com )向子域B页面(https://www.second.hao123.com)请求数据;

步骤:

1)在A页面与B页面的脚本中分别设置document.domain。

document.domain='hao123'

2) 在B页面中添加 Cookie。

document.cookie = "val=hello";

3) 在A页面中获取 Cookie。

var cookieFromSon = document.cookie;
console.log(cookieFromSon);//val=hello

优点:设置简单,通信方便。

缺点:仅适用于子域与父域不同,协议、顶级域、端口均相同。

2.window.location.hash

目标:A页面(https://www.hao123.com)向不同域 B页面(https://www.hao123456.com)请求数据。

步骤:

1)  在A页面添加指向B页面的 iframe 标签,并将参数添加到 # 后面 。

<iframe src="www.hao123456.com#123" id="b_iframe"></iframe> 

2)在B页面添加指向C页面(www.hao123.com/c.html,代理页面,与A页面同域)的 iframe 标签。

<iframe src="www.hao123.com/c.html#456" id="c_iframe"></iframe> 

3)在C页面(www.hao123.com/c.html)获取同域A页面的 hash 值并改变。

parent.parent.location.hash = self.location.hash.substring(1);
//parent即不同域B页面,parent.parent即同域A页面
//步骤分解:
//1.代理页面C页面获取不同域B页面的hash值(self.location.hash.substring(1))
//2.赋值给同域A页面(parent.parent.location.hash)

4)  在A页面获取数据。

window.onhashchange = function(){
   console.log(window.location.hash) //456
}

优点:不限制域。

缺点:需要通过代理页;双方都需要创建 iframe 标签;数据直接暴露在url中,数据容量和类型有限。

3.window.name

目标:A页面(https://www.hao123.com)向不同域 B页面(https://www.hao123456.com)请求数据。

步骤:

1) 在A页面添加指向B页面的 iframe 标签。

<iframe src="www.hao123456.com" id="iframe"></iframe>

2) 在 B页面的脚本中添加 window.name,以及跳转到与父页面同域的代理页;

window.name='123';//name长度在2MB
<a href="https://www.hao123.com/proxy.html">我是子页面,点我跳到与父页面相同域的代理页</a>

3) 同域的代理页有值之后即可在 A页面取值;

var val = document.getElement('iframe').contentWindow.name;
console.log(val);//'123';

优点:不限制域,window.name 容量很大,在2MB;

缺点:只能通过 iframe 标签跨域;需要借助代理页;需要实时监听 window.name 变化,影响性能。

4.WebSocket

WebSocket是一种通信协议,使用ws://和wss://作为协议前缀,该协议不受同源策略限制,只要服务器支持,都可以通信。

WebSocket请求头信息:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: https://www.hao123.com/

优点:因为有了Origin字段,所以不受同源策略限制,只要域名在白名单内,服务器就允许通信。

缺点:协议限制,仅适用于WebSocket协议。

5.window.postMessage

postMessage是HTML5的新特性,可以实现跨域通信。

目标:A页面(https://www.hao123.com)向不同域 B页面(https://www.hao123456.com)请求数据。

步骤:

1)在B页面发送postMessage。

window.opener.postMessage ('Hello World!', 'https://www.hao123.com');
//第一个参数为发送数据的内容,第二个参数为接收方地址,格式为协议+域名+端口,80端口可省略。

  2)在A页面接收数据。

window.addEventListener('message',function(e){console.log(e.data)},false);
//event 中有三个属性,
//e.data 传输过来的内容,
//e.source 发放方的窗口,
//e.origin 发送方的地址;
//source和origin结合可以反向发送数据,e.source.postMessage('我收到消息了',e.origin)。

优点:不限制域,实现简单。

缺点:发放方在不同环境下需要动态配置接收方的不同域名。

6.JSONP

JSONP的思想是在页面动态插入一个script标签,向服务器请求JSON数据,服务器收到请求后,将数据放在一个指定名字的回调函数中返回。

目标:客户端(https://www.hao123.com)向不同域 服务端(https://www.hao123456.com)请求数据。

步骤:

1)在客户端发起ajax请求。

//jQuery,会动态的创建script标签,提供一个回调函数作为参数拼接在服务端的地址上,并在拿到数据后销毁
//创建的标签如<script src="www.hao123456.com/user?callback=getData&id=1001"></script>
$.ajax({      
        url: 'www.hao123456.com/user',
        data:{id:'1001'},
        type:'GET',
        dataType: 'jsonp',
        jsonp: "getData",
        contentType: "application/json;charset=utf-8",
        success: function(data){
                //处理data数据
        }
});

2)服务端接收到请求,返回调用的回调函数。

//服务端调用回调函数
String jsonpCallback = request.getParameter("getData");
Map<String, Object> resp = new HashMap<String, Object>();
resp.put("val", "123");
return jsonpCallback + "(" + JSONObject.toJSONString(resp) + ")";

优点:不限制域,设置方便,支持老的浏览器。

缺点:只允许 GET 类型的请求;前后端需要协同回调函数名;发送的不是XHR请求,不能使用该请求的特性(如异步,事件)。

7.CORS

CORS(Cross-Origin Resource Sharing)是跨域资源共享,是W3C的标准,使用自定义的HTTP头部允许浏览器和服务器相互了解,从而决定是否通信。

目标:客户端(https://www.hao123.com)向不同域 服务端(https://www.hao123456.com)请求数据。

步骤:

1)在客户端发起 ajax 请求。

$.ajax({      
        url: 'www.hao123456.com/user',
        data:{id:'1001'},
        type:'POST',     
        contentType: "application/json;charset=utf-8",
        success: function(data){
                //处理data数据
        }
});

2)服务端设置HTTP请求头如下

response.addHeader("Access-Control-Allow-Origin", "https://www.hao123.com");//请求域地址,可设置*
response.addHeader("Access-Control-Allow-Methods", "POST");//请求方式
response.addHeader("Access-Control-Allow-Credentials", true);//是否允许发送Cookie,如果不允许,去掉这一行即可
response.addHeader("Access-Control-Allow-Headers", "Content-Type");//请求头设置
response.addHeader("Access-Control-Max-Age", "3600");//过期时间1分钟

优点:设置简单,支持多种请求方式。

缺点:IE6、IE7、Opera min 不支持CORS。

8.Ngnix

目标:客户端(https://www.hao123.com)向不同域 服务端(https://www.hao123456.com)请求数据。

步骤:

1)ngnix配置如下

server{
listen 80;
server_name localhost;#当前服务的域名
location /{
proxy_pass https://www.hao123.com/; #把所有的请求都转到www.hao123.com这个域名
add_header Access-Control-Allow-Methods *;
add_header Access-Contro-Max-Age 3600;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Headers $http_access_control_allow_headers;
#预检命令直接返回204
   if ($request_method = OPTIONS){
     return 204;
   }
 }
}

2)配置好之后重启 nginx -s reload

优点:不限制域,设置简单。

缺点:依赖于ngnix的配置。

注:在以上的解决方案中,用的最多的就是 window.postMessage、JSONP、CORS 和 Ngnix 这四种。

如果想要查看每一种方案区分对比,可以关注下一篇文章《跨域解决方案(二)对比篇》。 

 

  • 12
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值