跨域的几种方法

什么是跨域

浏览器出于安全方面的考虑,只允许客户端与本域(同协议、同域名、同端口,三者缺一不可)下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源,这被称为同源策略。

同协议:如都是http或者https
同域名:如都是http://xxx.com/ahttp://xxx.com/b
同端口:如都是8080端口

而有时候,我们不得不在一个客户端下访问不同域中的资源,于是需要用到一些方法来避开浏览器的同源策略,这些方法被称为跨域。
实现跨域有如下几种方法:

1. JSONP

JSONP(JSON with Padding)是数据格式JSON的一种使用模式,可以使网页实现跨域请求。其原理主要利用了 HTMLscript标签。由于script是采用开放策略,通过设置src引入不同域下的资源,所以可以通过script实现跨域,该方法需要后端支持。jsonp跨域的实现步骤如下:

  • 首先客户端定义一个数据处理函数fun,用于处理后端服务器返回的数据。
  • 创建一个script标签,并将scriptsrc设置为后端接口,并在最后加一个参数callback=fun
  • 服务端在收到由 script标签发出的请求后,会解析请求参数,获取callback对应的fun的字符串,然后将要返回的数据datafun拼接为一个'fun(data)'的字符串,返回客户端。
  • 客户端拿到响应后会放到script标签里执行,此时会调用fun函数,参数为data

下面来做个演示,首先为演示方便,将系统的hosts做如下修改:

127.0.0.1 example.a.com
127.0.0.1 example.b.com

服务器端(用server-mock启动,保存图片的url地址数据):

客户端(展示随机图片):

请求结果:


以上例子最终实现了由example.a.com到example.b.com的跨域。应注意的是,因为<script>只能发送GET请求,所以jsonp只能实现GET请求的跨域。如果希望能实现其他请求的跨域,就可以用接下来介绍的一种方法——CORS。

2.CORS

CORS(全称为:Cross-Origin Resouce Sharing)跨域资源共享,是一种通过ajax跨域请求资源的方法。浏览器将CORS请求分为两大类,简单请求(simple request)和非简单请求(not-so-simple request,浏览器对这两种请求的处理方式不一样。如果请求满足以下两个条件,则为简单请求。

  • 请求方式为HEAD,GET,POST三者中第一个。
  • HTTP头部信息不超过以下几种字段:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain。

简单请求的实现方式即当用XMLHttpRequest发请求时,浏览器如果发现该请求不符合同源策略,会给该请求加上一个请求头origin,origin用来说明本次请求来自哪个源(协议+域名+端口)。如果origin指定的源不在后台允许范围内,后台会返回一个正常的HTTP响应,然后浏览器会发现该响应头部信息不包含Access-Control-Allow-Origin字段,然后抛出一个错误,该错误被XMLHttpRequest的onerror函数捕获,响应被驳回,但因为该错误无法通过状态码识别,所以HTTP回应的状态码还是200。如果origin在后台允许范围内,则服务器返回的响应,会包含Access-Control-Allow-Origin:Origin(指定的源)信息,浏览器此时不会抛错,响应能正常处理。
非简单请求是是请求方法为PUT或DELETE,又或者Content-Type为application/json的对服务器有特殊要求的请求。非简单请求的CORS请求,会在正式通信前增加一次HTTP查询,称为预检(preflight),询问服务器当前网页所在域名是否在服务器的许可名单中,如果在,则发出正式的XMLHttpRequest,之后就与简单请求一样,不在则报错。
依旧用上面的例子。

服务器端(设置一个响应头,里面包含了允许请求的域名信息)

客户端(用ajax发请求)

最终实现的效果与第一个jsonp的例子一样。

3.降域

还有一种方式,就是通过降域来实现跨域。即通过设置document.domain的方式,将两个域名的domain设置为一个,如对于a.example.com和b.example.com,可以通过js设置document.domain = "example.com",实现跨域。
做个演示,假设在http://a.example.com:8080下有一个a.html文件,其中a.html中有一个iframe,它的srchttp://b.example.com:8080/b.html



正常情况下,由于a.html,b.html不同源,他们之间无法正常通信,但在设置document.domain = "example.com"后,两边可以互相通信。
最终效果如下:

用降域方法实现跨域操作简单,但是有一些缺点。比如域名只能往下设置,不能回去,比如从example.com回到a.example.com。同时如果一个子域名被攻击,多个被降域的域名都会被连带攻击,有很大的安全风险。

4.postMessage

postMessage是一个web API,可以实现跨域通信。window.postMessage()被调用时,会在所有页面脚本执行完毕后,向目标窗口派发一个MessageEvent消息。语法如下:

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

  • otherWindow表示目标窗口的一个引用,比如iframecontentWindow属性、执行window.open返回的窗口对象、或者是命名过或通过数值索引的window.frames
  • message表示需要发送到目标窗口的数据。
  • targetOrigin决定了哪些窗口可以接收消息,其值可以是字符串"*"(表示无限制)或者一个URI。
  • transfer是一串和message同时传递的Transferable 对象,这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

MessageEvent具有如下属性:

  • message属性表示该message的类型。
  • data属性为window.postMessage的第一个参数;
  • origin 属性表示调用window.postMessage()方法时调用页面的当前状态;
  • source 属性记录调用window.postMessage()方法的窗口信息。

用一个与上面降域类似的例子来做演示。同样有两个页面a.html和b.html,a.html中的iframesrc指向b.html。


最终实现a.html与b.html通信效果如下:

使用postMessage方法应注意的是,如果不希望从其他网站接收message,那么不要为message事件添加任何监听器。如果确实希望接收其他网站的message,那么应该始终使用origin和source属性来验证发件人的身份,以免被恶意的网站攻击。

小结

以上就是几种常见的跨域方法,各有优劣,且各自都有一定的安全问题,在日常应用中,需要有针对性的使用,对可能的安全风险采取相应措施。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • CORS 实现CORS需要浏览器和服务器的共同协作。对于前端开发者来说,CORS通信与同源的AJAX在代码方面没有...
    饥人谷_有点热阅读 1,270评论 0 2
  • 什么是跨域 说到跨域必须先解释什么是同源策略,它是由Netscape提出的一个著名的安全策略。浏览器出于安全方面的...
    8d2855a6c5d0阅读 1,322评论 0 0
  • 概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。 所有具有 src 属性的HTML标签都可以跨域 原...
    jeffAAA阅读 2,501评论 0 0
  • 同源策略 浏览器出于安全方面的考虑,为了保证用户信息的安全,防止恶意的网站窃取数据。只允许与本域下的接口交互。不同...
    祝余_scrapy阅读 397评论 0 0
  • 1. AJAX AJAX(Asynchronous JavaScript and XML),意思就是用JavaSc...
    公子七阅读 4,987评论 0 5