前端跨域方式大汇总

9 篇文章 0 订阅
8 篇文章 0 订阅

跨域问题

在几次的面试中,都被问到前端的跨域方案问题。在实际的项目中,也总是遇到。这篇文章是在看那些年,那些跨域问题 后的笔记。

在这篇文章中理解什么是跨域 JavaScript跨域(1):什么是跨域,如何跨域
具体策略限制情况可看下表

URL说明允许通信
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下允许
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不同文件夹允许
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名,不同端口不允许
http://www.a.com/a.js
https://www.a.com/b.js
同一域名,不同协议不允许
http://www.a.com/a.js
http://127.0.0.100/b.js
域名和域名对应ip不允许
http://www.a.com/a.js
http://script.a.com/b.js
主域相同,子域不同不允许
http://www.a.com/a.js
http://a.com/b.js
同一域名,不同二级域名(同上)不允许
http://www.a.com/a.js
http://www.b.com/b.js
不同域名不允许

前端跨域

  1. JSONP
  2. document.domain
  3. window.name
  4. window.postMessage

JSONP

JSONP 简介
原理
利用<script> 标签可以可以访问别的域名下的文件,并执行。
代码:在www.a.com 中请求www.b.com 的数据

script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.b.com/getdata?callback=demo';

定义jsonp的回调函数 与 callback参数定义的value同名称callback=demo

function demo(data){
	//处理数据
	console.log(data)
}

过程

  • a浏览器:发出请求http://www.b.com/getdata?callback=demo
  • b服务器,接到请求,JSONP技术中服务器会接受回调函数名作为请求参数callback=demo(这里是函数名是demo),把数据{data:‘message’} 包装好, 最终返回的是demo({data:‘message’})。
  • a浏览器获得 b服务器返回的数据demo({data:'message'}),其作为JavaScript代码来执行。
  • 只要在a的页面中 声明 demo 函数来处理数据就行。

jsonp的实现需要服务器配合:
服务端的实现(php):

//http://www.b.com/getdata
function getdata(){
	$callback = $this->getValue('callback');
	$data = ['a'=>1];
	if ($callback) {
         echo 'window.'.$callback.' && '.$callback.'(' . json_encode(['errcode' => 0,'data'=>$data]) . ')';
         //得到 window.demo && demo({"errcode":0,"data":{'a':1}})
     } else {
         echo json_encode(['errcode' => 0]);
     }
}

示例:

jQuery
在jQuery中 使用$.ajax() 方法时,将 dataType 设置为jsonp 即可。
代码如下:

$.ajax({
    url: 'http://www.b.com/getData.do',
	dataType: 'jsonp',
    jsonp: "callback",
    jsonpCallback: "dosomething"
    success: function(cb) {
        console.log(cb);
    },
    error: function(cb) {
        console.log(cb);
    }
});

这里找到了一些解释

参数:

  • dataType: ‘jsonp’,用于表示这是一个 JSONP 请求。
  • jsonp: ‘callback’,用于告知服务器根据这个参数获取回调函数的名称,通常约定就叫 callback。
  • jsonpCallback: ‘dosomething’,回调函数的名称,也是前面callback参数的值。

实际发送出来的完整请求长这样:http://www.b.com/getData.do?callback=dosomething&_=1471419449018。后面的随机字符串是jQuery加上的。

那些年,那些跨域问题 中说

不指定回调名,可省略callback参数,会由jQuery自动生成

如果没有指定 jsonCallback
如:

$.ajax({
  url: 'http://www.b.com/getdata?callback=?', //不指定回调名,可省略callback参数,会由jQuery自动生成
  dataType: 'jsonp',
  success: function(data) {
    console.log(data.msg);
  }
});

如果请求后拼参数callback=?,即没有指定回调函数名称, 则jQuery会动态创建一个值赋给这个?,服务端debug可以看到这个值,格式是这样的:
那么jQuery会创建一个值,会执行$.ajax内部的success方法或error方法 。
要注意的点

它只支持GET请求,这是由于该技术本身的特性所决定的。


document.domain

document.domain:获取/设置当前文档的原始域部分, 用于同源策略.

如,来自www.a.com想要获取document.a.com中的数据。只要基础域名相同,便可以通过修改document.domain为基础域名的方式来进行通信,但是需要注意的是协议和端口也必须相同。

//document.a.com
document.domain = "a.com";
//www.a.com
document.domain = "a.com"
var iframe = document.createElement('iframe');
iframe.src = 'http://document.a.com';
iframe.style.display = 'none';
document.body.appendChild(iframe);

iframe.onload = function() {
  var targetDocument = iframe.contentDocument || iframe.contentWindow.document;
  //可以操作targetDocument
}

HTML DOM IFrame 对象


window.name

原理

  • window.name属性就可以被共享
  • window.name这个全局属性是用来获取和设置窗口名称的

实践
在页面a.html( 8080端口)中 跨域拿b.html(8081端口 )的数据
b.html中设置 window.name 的值。

 <h1>这是页面B</h1>

    <script>
        var data = {
            name:'ziazan',
            age:'24'
        }
        window.name = JSON.stringify(data);//name属性只支持字符串,支持最大2MB的数据
        document.write('data in pageB window.name='+ window.name)
    </script>
<h1>这是页面A</h1>
    <script type="text/javascript">
        var iframe = document.createElement('iframe');
        // iframe.style.display = 'none';
        var isLoad = false
        document.body.appendChild(iframe);

        iframe.onload = function (){
            if(isLoad){
                var data = JSON.parse(iframe.contentWindow.name); //拿到name值
                document.write(data);

            }else{
                iframe.contentWindow.location = 'http://localhost:8081/html/ziazan/b.html';
                isLoad = true;
            }
        }
    </script>

运行结果:
运行效果

(注:iframe.contentDocument 与 iframe.contentWindow 的区别)

报错:

Uncaught DOMException: Blocked a frame with origin “http://localhost:8080” from accessing a cross-origin frame.

此方法失败,但是在同域下,做数据的传递还是可以的。


window.postMessage

window.postMessage(message,targetOrigin)
message: 要传递的对象,只支持字符串信息
targetOrigin:目标域,需要注意的是协议,端口和主机名必须与要发送的消息的窗口一致。如果不想限定域,可以使用通配符“*”,但是从安全上考虑,不推荐这样做。

示例
a.html( 8080端口)中 发送数据给b.html(8081端口 );

<h1>这是页面A,发送消息</h1>
    <button id="sendMsg">发送data</button>
    <br>
    <iframe id="iframe_b" src="http://localhost:8081/html/ziazan/b.html" frameborder="0"></iframe>
    <script type="text/javascript">
        var data = {
            name:'ziazan',
            age:'24'
        }
        var btn = document.getElementById('sendMsg');
        var iframe_b = document.getElementById('iframe_b').contentWindow;
        btn.addEventListener('click',function(){
                        iframe_b.postMessage(JSON.stringify(data),'http://localhost:8081/html/ziazan/b.html');
        })
    </script>
<h2>这是页面B,接受消息</h2>
    <div id="msg"></div>
    <script>
        window.addEventListener('message',function(e){
            document.getElementById('msg').innerHTML = e.data;
        })
    </script>

运行结果
这里写图片描述

疑问
现在是a.htmlb.html 发送消息,如果是页面a.html要获取b.html中的数据呢? window.postMessage 是不是只能是父到子的过程?

Hash

利用hash的改变不会触发页面修改的原理。

//页面A
var B = document.getElementByTagName('iframe');
B.src = B.src + '#' + 'data';

//页面B
window.onhashchange = function(){
	var data = window.loaction.hash
}

WebSocket

WebSocket 无同源限制

let ws = new WebSocket('wss://echo.websocket.org');

ws.onopen = function (evt) {
	console.log('Connection open ...');
	ws.send('Hello WebSockets');
}

ws.onmessage = function (evt) {
	console.log('Received Message:' + evt.data);
}

ws.onclose = function (evt) {
	console.log('Connection closed')
}

未完待续…

服务器跨域

1.CORS

1.允许来自任何域的访问

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 

2.设置资源可跨域访问

location ~* \.(eot|ttf|woff)$ {
          add_header Access-Control-Allow-Origin *;
          add_header Access-Control-Allow-Headers X-Requested-With;
          add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
      }

3.设置允许的域 (http://a.com 上配置 允许http://b.com 的访问)

location / {
		#允许跨域访问的域名,可以是一个域的列表,也可以是通配符*
		add_header 'Access-Control-Allow-Origin' 'http://b.com';
		 #是否允许请求带有验证信息
		add_header 'Access-Control-Allow-Credentials' 'true';
		#允许脚本访问的返回头
		add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
		 #允许使用的请求方法,以逗号隔开
		add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
		...
	}

2.反向代理

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
如果在 www.a.com 的页面 需要请求 www.b.com/apis/postData 接口。
在这里POST方式的Content-Type如果是是application/json的格式,算复杂请求(见简单请求和复杂请求),设置了CORS 也会有一个预请求。询问服务器是否支持此Content-Type。 这样性能不高,为了加速访问,可以在使用反向代理的方式进行跨域。

server {
		...
         location ^~ /apis/ {
            proxy_pass http://www.b.com/;  #注意域名后有一个/
        }
        ...
    }

参考

Nginx 通过 CORS 实现跨域
Nginx反向代理解决跨域问题
跨域资源共享 CORS 详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值