引子
不管是在日常开发还是在面试过程中,我们时常会遇到跨域问题,下面谈谈个人的解决方案及观点,有不足之处敬请指出。
使用工具跨域
应用场景:在开发环境下,我们一般采取前后端分离,前端部署在某一服务器或本地,而后端又部署在另外的服务器上,当前端调用后端的接口时则会出现跨域问题。
解决方案:
(1) 设置浏览器:
对于chrome:新建一个快捷方式 => 右击快捷方式 => 点击属性 => 在目标栏追加“ --disable-web-security --user-data-dir=D:\work\chrome” => 点击确定即可
注:对于其他浏览器的跨域方法,目前尚不了解
(2) 使用Nginx
将前端页面部署在Nginx服务器上,让后在配置的location上添加proxy_pass即可。如:
location ^~ /expense/ { proxy_pass }
其中:expense 表示的是上下文,proxy_pass后面跟的则是反转的地址
(3) 使用http-proxy-middleware
此方法主要针对的是node开发环境,具体怎么操作我就不介绍了,看看下面这个栗子你应该秒懂了:
通过代码实现
我想如果在面试中被问到这个问题,面试官最想要的答案应该是这个了。
(1) JSONP
原理:
其原理是根据XmlHttpRequest 对象受到同源策略的影响,而 <script> 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。
不足:
只能使用 GET 方法发起请求,这是由于 script 标签自身的限制决定的。
(2) 使用Image对象进行跨域
这种跨域方法主要用来统计数据,以为它没有回调处理方法,具体操作方法很简单,首先要新建一个Image对象,然后为该对象的src属性赋值即可,如:
(3) CORS方法
原理:
Cross-OriginResource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。
实现:
此种方法的实现在后台,如:
CORS中的属性
Access-Control-Allow-Origin:The origin parameter specifies a URI that may access the resource.The browser must enforce this. For requests without credentials, the server mayspecify “*” as a wildcard, thereby allowing any origin to access the resource.
Access-Control-Allow-Methods:Specifies the method or methods allowed when accessing the resource.This is used in response to a preflight request. The conditions under which arequest is preflighted are discussed above.
Access-Control-Allow-Headers: Used in response to a preflight request to indicate which HTTPheaders can be used when making the actual request.
(4) window.name
原理:
使用window.name来跨域其实只是使用它来传输数据,因为window.name有个美妙之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
操作方法:
在页面中添加一个iframe(即会创建一个对应的window对象,当页面加载另一个新的页面时,window的name属性是不会变的)
在数据页面将需要的数据赋值给window.name。
将iframe再加载一个与承载页面同域的空白页面(因为承载iframe的parent页面不能直接访问不在同一域下iframe的name属性)
对window.name进行数据读取
栗子:
www.test.com下a.html页:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <button>click!</button> <script type="text/javascript"> var a = document.getElementsByTagName("button")[0]; a.onclick = function() { // [1] var inf = document.createElement("iframe"); inf.src = "http://www.domain.com/window.name/b.html" + "?h=5"; var body = document.getElementsByTagName("body")[0]; body.appendChild(inf); inf.onload = function() { // [3] inf.src = 'http://www.test.com/b.html'; // [4] console.log(inf.contentWindow.name); body.removeChild(inf) } } </script> </body> </html>
www.domain.com下b.html页:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <a href="" target="" title="">2</a> <script type="text/javascript" src="../cross/jquery-2.2.3.js"> </script> <script> var str = window.location.href.substr(-1, 1); $.ajax({ type: "get", url: "http://www.domain.com/a.php" + "?m=" + str, async: true, // [2] success: function(res) { window.name = res; }, error: function() { window.name = 'error'; } }); </script> </body> </html>
(5) window.postMessage()
原理:
window.postMessage是HTML5最常用的API之一,实现两个不同域窗口对象之间的数据通信。
具体操作方法如下:
在发送数据窗口执行:otherWindow.postMessage(msg,origin)
otherWindow: 表示接受数据的窗口的window对象,包括iframe的contentWindow和通过window.open打开的新窗口。
msg: 表示要发送的数据,包扩字符串和对象(ie9以下不支持,可以利用字符串和json互换)。
origin:表示接收的域名。
在接受的窗口监听window的message事件,回掉函数参数接受一个事件对象event,包括的属性有:
data:接受的数据
origin:发送端的域
source:发送端的DOMWindow对象
栗子:
在父框架页面index.html发送obj对象给远程服务器的wozien.com/test/b.html,该页面是通过iframe加载的,如下:
<!DOCTYPE html> <html> <head> <title>window.postMessage</title> </head> <body> <iframe id="proxy" src="http://wozien.com/test/b.html" onload="postMsg()" style="display: none"></iframe> <script type="text/javascript"> var obj = { msg: 'this is come from client message!' } function postMsg() { var iframe = document.getElementById('proxy'); var win = iframe.contentWindow; win.postMessage(obj, 'http://wozien.com'); } </script> </body> </html>
在远程页面b.html中监听message事件,先通过origin属性判断下数据来源的域是否可信任,加强安全措施。具体代码如下:
<!DOCTYPE html> <html> <head> <title></title> <script type="text/javascript"> window.onmessage = function(e) { if (e.origin !== 'http://localhost') return; console.log(e.origin + ' ' + e.data.msg); } </script> </head> <body> <p>this is my server</p> </body> </html>
(6) 修改document.domain来跨子域
首先来解释一下document.domain,domain属性可返回下载当前文档的服务器域名。对domain属性进行修改是有限制的,即只能将其修改成当前域名的上级域名,如:
www.sojson.com下指到sojson.com 是可以的;
www.baidu.com 下指到 baidu.com 是可以的;
www.sojson.com下指到abc.sojson.com 不可以;
www.sojson.com下指到www.baidu.com 不可以;
从上面中的栗子中可见通过修改document.domain 来实现跨域是有条件限制的,即它们额二级域名必须相同。
明白了这些,具体怎么做,也就很清楚了。就是在两个相关的页面中将domain改成一样即可,栗子不在写了。
好了,跨域的方案到这里也就整理得差不多了,有什么不对或不足的地方希望指出,我在这里先谢过了。