前端跨域

来源:互联网 发布:mac上用word办公软件 编辑:程序博客网 时间:2024/06/05 20:17

跨域问题

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

在这篇文章中理解什么是跨域 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';

过程

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

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.comdocument.domain = "a.com";
//www.a.comdocument.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端口 )的数据

<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>

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>

运行结果:
运行效果

(注: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 是不是只能是父到子的过程?

未完待续…

服务器跨域

  1. 反向代理
  2. CORS
  3. 优化 CORS
0 0
原创粉丝点击