前端跨域原理和常见解决方式

来源:互联网 发布:网络采购的方式主要有 编辑:程序博客网 时间:2024/05/23 15:45

理解跨域必须先了解同源策略,所以——

何谓同源策略(same origin policy)?

浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本对不同域的服务进行跨站调用。简单来讲就是,从一个域上加载的脚本不允许访问另外一个域的文档属性。

何谓同源

URL由协议、域名、端口和路径组成,当三同——同协议(protocol)、同域名/IP(domain)、同端口(port)的时候,则表示同源。

URL 说明 是否允许通信 http://www.baidu.com/a.js
http://www.baidu.com/b.js 同一域名 允许 http://www.baidu.com/aaa/a.js
http://www.baidu.com/bbb/b.js 同一域名不同文件 允许 http://www.baidu.com:8000/a.js
http://www.baidu.com:3000/b.js 同一域名不同端口 不允许 http://www.baidu.com
https://www.baidu.com 同一域名不同协议 不允许 http://www.baidu.com
https://www.google.com 主域相同,子域不同 不允许 http://www.baidu.com
https://aaa.bbb.ccc 不同域名 不允许 http://www.aaa.com
https://aaa.com 同一域名,不同二级域名 不允许

上表中的3、4、5、6、7种情况都会造成跨域请求,也就是违背了同源策略。而这些跨域请求并非是浏览器限制了发起跨站请求,而是请求可以正常发起,到达服务器端,但是服务器端返回的结果会被浏览器拦截。

跨域调用的解决方式:

1. jsonp

那就又有必要先提一下json的概念了,json是一种轻量级的数据传输格式,因为json格式数据的编码和解析基本在所有主流语言中都被实现,所以大部分前后端分离的架构都以json格式进行数据传输。

那jsonp又是什么呢?

jsonp(json padding),json数据的包装,形如:callback({“name”:”zhangyangyang”,”major”:”network”}),或者我们也可以简单理解为带有callback(回调函数)的json即为jsonp。也就是说,jsonp里传输的就是json的数据格式。只是在发送请求时多加一个参数,值为callback(回调函数)。后台程序在获得该回调函数后,把准备好的json数据拼接到callback里面。

原理很简单,就是利用script标签没有跨域限制的“漏洞”,来达到与第三方通讯的目的。在需要通讯时,本站脚本创建一个script标签,地址指向第三方的API网址,形如: <script src = "http://www.baidu.com/api?param1=aaa&param2=bbb"></script> , 并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。这样浏览器就会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理传入的数据。

下面就可以撕一波jsonp代码了~

function jsonp(options){    // 整理参数    options = options || {};    options.data = options.data || {};    options.timeout = options.timeout || 0;    // 随机回调函数的名字    var callbackName = ' jsonp ' + Math.random();    callbackName = callbackName.replace('.', ' ');  //函数名不可含有.    options.data[options.callback] = callbackName;    // 拼接url    var arr = [];    for(var key in options.data){        arr.push(key + ' = ' + encodeURIComponent(options.data[key]));    }    options.url = options.url + ' ? ' + arr.join('&);    // 添加script标签    var script = document.createElement("script");    script.type = "text/javascript";    script.src = options.url;    document.body.appendChild(script);    // 设定超时    if(options.timeout){        var timer = setTimeout(function(){            document.body.removeChild(scipt);            window[options.data[options.callback]] = function(){                options.err && options.error();            }        },options.timeout);    }    // 定义回调函数    window[options.data[options.callback]] = function(json){        clearTimeout(timer);        options.success && options.success(json);        document.body.removeChild(script);   //清除用过的script标签        window[options.data[options.callback]] = null;  //释放window头上用过的属性    }}

2.Ajax

jQuery已经有集成的近乎完美的$.ajax也可以实现jsonp的跨域调用,我在前面博客也有用原生js去实现jQuery的ajax,详情请戳原生js封装Ajax.

[注]:两者的实现大同小异,但是实现原理并不相同,jsonp跨域调用不是通过XMLHttpReaquest对象,而是通过动态创建script标签,所以再实现原理上,jsonp和Ajax已经没有一点关系。看上去形式相似,只是由于jQuery对jsonp做了封装和转换。

jsonp和Ajax的区别:

1. jsonp只能使用GET方法发起请求,这是由于script标签自身的限制决定的。

2.jsonp由于不是通过XMLHttpRequest进行传输,所以不能注册success和error等事件监听函数。

3.CORS

什么是CORS?

Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 jsonp 模式的现代版。与 jsonp 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比jsonp 要来的好。另一方面,jsonp 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。

CORS实现——

app.post('/cors', function(req, res) {    res.header("Access-Control-Allow-Origin", "*");    res.header("Access-Control-Allow-Headers", "X-Requested-With");    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");    res.header("X-Powered-By", ' 3.2.1')    res.header("Content-Type", "application/json;charset=utf-8");    var data = {        name: req.body.name + ' - server 3001 cors process',        id: req.body.id + ' - server 3001 cors process'    }    console.log(data)    res.send(data)    res.end()})

在服务器中对返回信息的请求头进行设置:

1.Access-Control-Allow-Origin

2.Access-Control-Allow-Methods

3.Access-Control-Allow-Headers

jsonp与CORS的区别:

1.CORS 除了 GET 方法外,也支持其它的 HTTP 请求方法如 POST、 PUT 等

2.CORS 可以使用 XMLHttpRequest 进行传输,所以它的错误处理方式比 jsonp 好

3.jsonp 可以在不支持 CORS 的老旧浏览器上运作

4.document.domain

比如,有一个页面,它的src是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://script.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。但是我们只要把http://www.example.com/a.html 和 http://script.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。

   // http://www.example.com/a.html     document.domain = 'example.com';   var ifr = document.createElement('iframe');   ifr.src = ' http://script.com/b.html ';   ifr.style.display = 'none';   document.boody.appendChild(ifr);   ifr.onload = function(){       var doc = ifr.contentDocument || ifr.contentWindow.document;       console.log(doc);  //获取到的页面数据   }
    http://script.com/b.html    document.domain = 'example.com';

5.window.postMessage()

这个方法是 HTML5 的一个新特性,可以用来向其他所有的 window 对象发送消息。需要注意的是我们必须要保证所有的脚本执行完才发送 MessageEvent,如果在函数执行的过程中调用了他,就会让后面的函数超时无法执行。

postMessage()允许来自不同源的脚本采用异步方式进行通信,可以实现跨域消息传递。

postMessage(data,origin)

data:要传递的数据

origin:指明目标窗口的源。协议主机端口号

接收消息:监听window的message事件,包括

1.data——传递过来的message

2.source——发送消息的窗口对象

3.origin——发送消息窗口的源

6.window.name

window对象有个name属性,该属性有个特征:即在一个窗口 (window) 的生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

end···


阅读全文
0 0
原创粉丝点击