• 引子

不管是在日常开发还是在面试过程中,我们时常会遇到跨域问题,下面谈谈个人的解决方案及观点,有不足之处敬请指出。

  • 使用工具跨域

应用场景:在开发环境下,我们一般采取前后端分离,前端部署在某一服务器或本地,而后端又部署在另外的服务器上,当前端调用后端的接口时则会出现跨域问题。

解决方案:

(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开发环境,具体怎么操作我就不介绍了,看看下面这个栗子你应该秒懂了:

wKiom1lHjR-hgbJJAABI_wBBphg089.png

 

  • 通过代码实现

我想如果在面试中被问到这个问题,面试官最想要的答案应该是这个了。

 

(1)       JSONP

原理:

其原理是根据XmlHttpRequest 对象受到同源策略的影响,而 <script> 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。

不足:

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

 

(2)       使用Image对象进行跨域

这种跨域方法主要用来统计数据,以为它没有回调处理方法,具体操作方法很简单,首先要新建一个Image对象,然后为该对象的src属性赋值即可,如:

wKioL1lHjpPhqS64AAAvHcmXy58017.png

(3)       CORS方法

原理:

Cross-OriginResource SharingCORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。

实现:

此种方法的实现在后台,如:

wKioL1lHjtSS95BJAAB_l1NDg5w287.png

         CORS中的属性

         Access-Control-Allow-OriginThe 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-MethodsSpecifies 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)。

操作方法:

  1. 在页面中添加一个iframe(即会创建一个对应的window对象,当页面加载另一个新的页面时,windowname属性是不会变的)

  2. 在数据页面将需要的数据赋值给window.name

  3. iframe再加载一个与承载页面同域的空白页面(因为承载iframeparent页面不能直接访问不在同一域下iframename属性)

  4. window.name进行数据读取

栗子:

www.test.coma.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.comb.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.postMessageHTML5最常用的API之一,实现两个不同域窗口对象之间的数据通信。

具体操作方法如下:

  1. 在发送数据窗口执行:otherWindow.postMessage(msg,origin)

otherWindow 表示接受数据的窗口的window对象,包括iframecontentWindow和通过window.open打开的新窗口。

msg 表示要发送的数据,包扩字符串和对象(ie9以下不支持,可以利用字符串和json互换)

origin:表示接收的域名。

  1. 在接受的窗口监听windowmessage事件,回掉函数参数接受一个事件对象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" οnlοad="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.domaindomain属性可返回下载当前文档的服务器域名。对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改成一样即可,栗子不在写了。

 

好了,跨域的方案到这里也就整理得差不多了,有什么不对或不足的地方希望指出,我在这里先谢过了。