文档章节

web跨域通信的几种解决方案

x
 xszl
发布于 2017/08/16 15:47
字数 1298
阅读 12
收藏 0

##浏览器同源策略 做web开发的同学应该都知道浏览器禁止跨域访问,这是因为浏览器设定的同源策略,这条策略由网景公司于1995年引入,限制从一个源加载的文档或脚本与来自另一个源的资源进行交互,目的是为了隔离潜在恶意文件,保证网站的安全。那么什么是所谓的"同源",简单来说,就是 协议域名端口 三者都相同(IE有几条例外,但都是非标准,暂不讨论)。

同源策略能提高web网站的安全性,但有时候我们又不得不去请求其他域下的资源、数据,这个时候就要通过各种方式来解决这个问题。 ##解决方案

###document.domain

  • 在根域范围内,可以将domain属性的值设置为它的上一级域,向下设置或者设置成其他域是不可以的,例如,在 developer.mozilla.org 域内,可以把domain设置为 "mozilla.org" 但不能设置为 "mozilla.com" 或者"org"。

  • document.domain常用于同站不同域的情况。相同主域名不同子域名下的页面,可以设置 document.domain 让它们同域,例如 www.itouzi.combbs,itouzi.com ,设置两者的 document.domain = 'itouzi.com',可以实现两页面的通信。下述代码如果不设置document.domain会报错

    // www.itouzi.com/test.html
    document.domain = 'itouzi.com';
    var _iframe = document.createElement('iframe');
    _iframe.src = 'https://bbs.itouzi.com/test.html';
    _iframe.onload = function() {
      var _iframeDoc = _iframe.contentDocument || _iframe.contentWindow.document;
      console.log(_iframeDoc);
    }
    document.body.appendChild(_iframe);
    
    // bbs.itouzi.com/test.html
    document.domain = 'itouzi.com';
    

###window.name window对象有个name属性,该属性有个特征:无论是否同源,即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。前一个网页设置了这个属性,后一个网页可以读取它。(传递HASH和这种方法类似,当前窗口共享URL的#号后面的部分)

    // www.itouzi.com/test.html 应用页面
    function requestData(url, successCB) {
        var _status = 0;
        var _iframe = document.createElement('iframe');
        _iframe.setAttribute("style", "width: 0; height: 0; border: none;");
        _iframe.setAttribute("src", url);
        _iframe.onload = function() {
            if (_status == 1) {
                var data = _iframe.contentWindow.name;
                if (data) {
                    successCB && successCB(data);
                }
                _iframe.parentNode.removeChild(_iframe);
            } else if (_status == 0) {
                _status = 1;
                _iframe.contentWindow.location = "https://www.itouzi.com/proxy.html";
            }
        };
        document.body.appendChild(_iframe);
    }
    var _url = 'https://bbs.itouzi.com/test.html';
    var _successCB = function(data) {
        console.log(data);
    };
    requestData(_url, _successCB);
    // bbs.itouzi.com/test.html 数据页面
    // 传输的数据
    window.name = 'hello world';
    // www.itouzi.com/proxy.html
    // 只需保证和应用页面一个域下即可,不需要任何内容

###window.postMessage html5引入的一个跨文档通信 API,可以安全地实现跨源通信。window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent 消息。 该MessageEvent消息有三个属性需要注意: data 属性为 发送的消息;origin 属性表示调用window.postMessage() 方法的页面的源; source 属性记录调用 window.postMessage() 方法的窗口信息。

发送方调用window.postMessage

otherWindow.postMessage(message, targetOrigin, [transfer]);

#####otherWindow 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。 #####message 将要发送到其他 window的数据。 #####targetOrigin 接收消息的窗口的源(origin),通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的orign属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是"*"。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。 #####transfer 可选参数,是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接受消息窗口可以通过 message 事件监听消息(IE8及以下版本请使用 attachEvent

window.addEventListener('message', function(e) {
  console.log(e.data);
}, false);

message事件的事件对象event的属性有 #####data 从其他 window 中传递过来的消息内容。 #####origin 调用 postMessage 时消息发送方窗口的 origin #####source 对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。

    // www.itouzi.com/test.html
    var popup = window.open('https://bbs.itouzi.com/test.html');
    document.getElementById('send').addEventListener('click', function() {
        popup.postMessage('hello', 'https://bbs.itouzi.com/test.html');
    }, false);
    // bbs.itouzi.com/test.html
    window.addEventListener('message', function(e) {
        // 过滤
        if (e.origin != 'https://www.itouzi.com') {
            return;
        }
        console.log(e);
    }, false);

###src标签 具有src(或者href)属性的HTML标签是可以跨域的,包括<img>、<script>、<iframe>、<link>。在document.body中append一个具有src属性的HTML标签, src属性值指向的URL会以GET方法被访问,该访问是可以跨域的。一些统计就是采用这种方式。(下图引自MDN) 输入图片说明 ###服务器地址映射 需要在web服务上做下代理

location /ad {
	proxy_pass http://ad.devsai.com
}

###JSONP 普通的ajax请求只能请求同源的接口,但是使用JSONP可以实现服务器与客户端跨源通信。利用script标签的跨域,将要请求的接口设置成srcipt的src属性,然后再将这个script标签append到文档中。通过动态插入script,向跨源接口发出请求然后将接口返回的数据放在一个指定名字的函数(回调函数)里作为函数参数传回,script标签请求到的内容就会被当做js代码执行。客户端需要做的就是提前定义好这个回调函数。需要注意的是和所有依赖于创建HTML标签的方式一样,JSONP不支持POST,只支持GET方法。

    // www.itouzi.com/test.html
    function successCB(data) {
        console.log(data);
    }
    function corsFunc(src) {
        var _script = document.createElement('script');
        _script.src = src;
        document.body.appendChild(_script);
    }
    var _url = 'https://bbs.itouzi.com/test.php?jsoncallback=successCB';
    corsFunc(_url);
    // bbs.itouzi.com/test.php
    <?php
    $func = $_REQUEST['jsoncallback'];
    $res = array(
        'data' => 'hello',
        'code' => 0,
        'info' => 'sucess'
    );
    header("Content-type:application/json; charset=utf-8");
    echo $func."(".json_encode($res).")";

###CORS CORS是一个W3C标准,全称“跨域资源共享”(Cross-origin resource sharing)。跨域资源共享标准通过新增一系列 HTTP 头,让服务器能声明哪些来源可以通过浏览器访问该服务器上的资源。

  • 头部设置

    对于简单请求,浏览器发出CORS请求,会在头部信息中增加一个 Origin 字段,服务器接收到请求,检测这个字段如果在指定范围内,那么会在响应中增加Access-Control-Allow-Origin等字段;如果不在指定范围内,那么依然会返回一个正常的HTTP回应,只是响应头中没有Access-Control-Allow-Origin字段,浏览器发现之后就会抛出一个错误被XMLHttpRequest的onerror回调函数捕获。 #####Access-Control-Allow-Origin 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。 #####Access-Control-Allow-Credentials 指定是否允许请求带上Cookie和HTTP认证信息等消息。服务端和客户端均需要设置,需注意的是,如果要发送Cookie(设置了the credentials flag为true),那么Access-Control-Allow-Origin就不能使用”*“,必须指定明确的、与请求网页一致的域名。

  • 请求方式 #####简单请求 请求方式是GET/POST/HEAD,content-type是application/x-www-form-urlencoded、multipart/form-data、text/plain中的一种。 #####预请求 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。服务器会做检测,如果没有通过,浏览器会有相应的错误信息。预请求通过后,后面就同简单请求一致了。

  • 兼容性

    can i use 上,IE11以下是不支持的。

同JSONP相比,CORS支持所有类型的HTTP请求,但是缺点就是存在一定的兼容性。 ###WebSocket WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

##总结 以上几种跨源通信方案,各自有各自的优缺点,具体选取还是要结合实际需求

  • document.domain:仅支持同个域下的子域跨域,跨域能力有限,虽然主流浏览器都支持(IE6+、Firefox、Chrome等),不过更改document.domain,会有一系列的副作用,为后续的工作留下隐患。
  • window.name:window.name容量很大(2M左右),可用于存储很长的字符串,缺点是接口返回的内容必须都是html里嵌入的script脚本。
  • window.postMessage:兼容问题,IE11以下均有所限制。
  • src标签:只能用于GET请求
  • 服务端地址映射:需要说服服务端同学
  • JSONP:是之前最常用的解决跨域请求的方法,但是不能用于POST请求
  • CORS:目前比较常用的解决跨域请求的方法。存在兼容性问题
  • WebSocket:需要服务器支持,而且主要目的也是不为了跨源

© 著作权归作者所有

x
粉丝 0
博文 14
码字总数 17267
作品 0
私信 提问
HTML5 WebMessaging 体验

下载演示文件 - 2.97 KB 简介 作为WEB开发者,经常会遇到一个问题: 跨域通信和“同源政策”的协调化。因为 JavaScript 代码不能访问位于不同 域(或子域) 或 协议 (HTTP/HTTPs) 或 端口 的代...

oschina
2014/04/29
705
0
常见跨域解决方案以及Ocelot 跨域配置

常见跨域解决方案以及Ocelot 跨域配置 Intro 我们在使用前后端分离的模式进行开发的话,如果前端项目和api项目如果不是一个域名下的话往往会有跨域问题。今天来介绍一下我们在Ocelot网关配置...

WeihanLi
05/11
0
0
web跨域解决方案

阅读目录 什么是跨域 常用的几种跨域处理方法: 跨域的原理解析及实现方法 总结 摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让自...

幕三少
2016/09/01
0
0
Spring MVC通过CROS协议解决跨域问题

现在接手学校网络中心的一个项目,根据团队成员的实际情况以及开发需要,老师希望做到前后端完全分离。后台使用java提供restful API 作为核心,前台无论PC或者移动端可以共用一个核心。前期解...

vstaryw
2016/07/18
146
0
JavaScript跨域相关的总结

一、同源策略 同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击...

影子同学
05/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

可见性有序性,Happens-before来搞定

写在前面 上一篇文章并发 Bug 之源有三,请睁大眼睛看清它们 谈到了可见性/原子性/有序性三个问题,这些问题通常违背我们的直觉和思考模式,也就导致了很多并发 Bug 为了解决 CPU,内存,IO ...

tan日拱一兵
27分钟前
2
0
网络七层模型与TCP/UDP

为了使全球范围内不同的计算机厂家能够相互之间能够比较协调的进行通信,这个时候就有必要建立一种全球范围内的通用协议,以规范各个厂家之间的通信接口,这就是网络七层模型的由来。本文首先...

爱宝贝丶
30分钟前
2
0
Jenkins World 贡献者峰会及专家答疑展位

本文首发于:Jenkins 中文社区 原文链接 作者:Marky Jackson 译者:shunw Jenkins World 贡献者峰会及专家答疑展位 本文为 Jenkins World 贡献者峰会活动期间的记录 Jenkins 15周岁啦!Jen...

Jenkins中文社区
48分钟前
8
0
杂谈:面向微服务的体系结构评审中需要问的三个问题

面向微服务的体系结构如今风靡全球。这是因为更快的部署节奏和更低的成本是面向微服务的体系结构的基本承诺。 然而,对于大多数试水的公司来说,开发活动更多的是将现有的单块应用程序转换为...

liululee
今天
7
0
OSChina 周二乱弹 —— 我等饭呢,你是不是来错食堂了?

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @ 自行车丢了:给主编推荐首歌 《クリスマスの夜》- 岡村孝子 手机党少年们想听歌,请使劲儿戳(这里) @烽火燎原 :国庆快来,我需要长假! ...

小小编辑
今天
745
11

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部