web前端跨域的一些解决方案

没有归纳之前对跨域的一些说法是模糊的,什么jsonp啊,跨域原理啊,心里只有一个大概的说法,知道这个东西,然后用的时候直接百度Ctrl+C,后来闲下来决定整理一波这些知识点,需知其所以然。

模糊状态

什么是跨域

  • 域名相同

  • 端口号相同

  • 协议相同
    那么以上条件只要有一个不同,都被当作是不同的,会出现跨域的问题。
    为什么会出现这个问题呢?因为浏览器的同源策略。简单来说:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。这个安全机制大致限制了两个方面:

  • 一是针对接口的请求CSRF攻击。某天你登录了你的银行账户操作XXX,那么在你访问某些非法网站的时候,如果没有同源策略,它向银行的接口发起请求(浏览器会自动带上相关的cookie),假设银行的服务端是根据cookie来判断登录状态的话,非法网站就相当于登录了你的银行账户......

  • 二是针对Dom的查询。某天你邮箱收到了一个邮件,说你的银行账户有危险,然后你点击进去一看,跟以前登录的页面一模一样,你立马输入了你的账号密码......这个页面其实是一个钓鱼网站,里面内嵌了一个iframe

<iframe src="某银行网页"></iframe>
// 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
const iframe = window.frames['yinhang']
const node = iframe.document.getElementById('你输入账号密码的Input')
console.log(node) // ok,拿到密码,跑路

那么,其实这是浏览器对我们的一种保护机制,把坏人挡在门外。那么,问题来了,我们怎么确定门外的人到底是好人还是坏人呢?浏览器关上了坏人的一扇门,留给了我们好人一扇窗。

好人的跨域方式

好人卡

接口请求之JSONP

JSONP跟JSON没有关系..就好像JavaScript和Java一样
浏览器对script、img(这些标签的请求方式都是GET,所以jsonp不支持POST)这种标签没有限制,我们就可以这样干

前端代码
<script type='text/javascript'>
后端返回直接执行的方法,相当于执行这个方法,由于后端把返回的数据放在方法的参数里,所以这里能拿到res。
cb = function(res) {
    console.log(res)
}
</script>
<!-- 传入数据是msg,传入一个回调方式cb -->
<script src='http://xxxxx/api/jsonp?msg=helloWorld&cb=cb' type='text/javascript'></script>

服务端类似这样返回
static async jsonp(ctx) {
    // 获取前端传入的参数
    const query = ctx.request.query
    // query.cb获取到前端传入的cb字段。
    // 由于前端是用script标签发起的请求,
    // 所以前端请求到的资源其实是这样的一段js代码  cb(服务端返回的数据),所以前端就直接执行了
    ctx.body = `$ {query.cb} (服务端返回的数据)`
}

接口请求之CORS跨域

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。更多有关跨域资源共享 CORS 的知识

浏览器中可以查看对应的响应头,举个例子,如下


栗子

服务端允许CORS,服务端需要针对接口设置的一系列响应头 (Response Headers)

该字段必需。设置允许请求的域名,多个域名以逗号分隔,也可以设置成 * 即允许所有源访问
Access-Control-Allow-Origin:  http://www.YOURDOMAIN.com
该字段必需。设置允许请求的方法,多个方法以逗号分隔
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
该字段可选。设置允许请求自定义的请求头字段,多个字段以逗号分隔
Access-Control-Allow-Headers: Authorization
该字段可选。设置是否允许发送 Cookies
Access-Control-Allow-Credentials: true                              

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

1.简单请求
目前大多数情况都采用这种方式。简单请求只需要设置Access-Control-Allow-Origin即可。满足以下两个条件,就属于简单请求。

  • 请求方法是这三种方法之一:HEAD,GET,POST
  • HTTP头不超出一下几种字段
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

2.非简单请求
非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。来看栗子:


204预检测

非简单请求需要根据不同情况配置不同的响应头,一系列响应头配置项见上方

接口请求之使用代理跨域

这个说法相信不陌生,我们依然使用前端域名请求,然后有一个中介商---代理把这个请求转发到真正的后端域名上,那也就不存在跨域问题了。
比较普遍的Nginx,简单的配置一下就可以了。了解更多的配置信息:nginx详解

server{
    # 监听9099端口
    listen 9099;
    # 本地的域名是localhost
    server_name localhost;
    #凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://baidu.com
    location ^~ /api {
        proxy_pass http://baidu.com;
    }    
}

然后前端这边的请求地址是http://localhost:9099/api/xxx,然后Nginx监听到地址是localhost:9099/api的请求,就帮我们转发到真正的服务端地址http://baidu.com

CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及在服务端同意jsonp方式时,可以向不支持CORS的网站请求数据。Nginx可以说是最方便的,不过需要部署Nginx才行,需要对服务器有一定的理解,不太适合刚入门的同学,当然也可以请后台同学帮忙部署。

好像懂了点什么

跨窗口操作DOM之postMessage

window.postMessage(data,origin) 是HTML5的一个接口,专注实现不同窗口不同页面的跨域通讯。

参数 描述
data 要传递的数据
origin 字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

现在是这么一个情况,由于同源策略的限制下,a.html不能操作iframe(b.html)里面的dom,那么使用postMessage就可以解决这一情况

<html>
<div>
    <button @click="postMessage">给iframe发消息</button>
    <iframe id="myIframe" src="b.html"></iframe>
</div>
<script>
var myIframe = document.getElementById('myIframe');
myIframe.contentWindow.postMessage('hello world!', 'b.html');
</script>
</html>

然后b.html页面通过message事件监听并接受消息:

window.addEventListener('message',function(event) {
    var data = event.data; //消息  
    var origin = event.origin; //消息来源地址  
    var source = event.source; //消息来源的Window对象
    // 为了安全性,一定要对来源做校验
    if (origin == "a.html") {
        alert(data); //hello world!
        source.postMessage('回信啦', 'a.html'); // a.html页面也用message方法接收就行
    }
});

跨窗口操作DOM之document.domain

这种方式只适合主域名相同,但子域名不同的iframe跨域。
比如主域名是http://baidu.com/:8888,子域名是http://child.baidu.com/:8888,这种情况下给两个页面设置相同的document.domain即document.domain = baidu.com就可以访问各自的window对象了。

参考文章

前端跨域整理
不要再问我跨域的问题了

读后感

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

推荐阅读更多精彩内容

  • 1. 什么是跨域 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScri...
    cbw100阅读 6,325评论 2 86
  • <转>详解跨域(最全的解决方案) 什么是跨域跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,...
    涅槃快乐是金阅读 4,725评论 0 3
  • 原文地址:原文地址 什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 广义...
    C_Y大渔阅读 1,258评论 1 13
  • 前端开发中,跨域使我们经常遇到的一个问题,也是面试中经常被问到的一些问题,所以,这里,我们做个总结。小小问题,不足...
    Nealyang阅读 470评论 0 0
  • 所有的美好 若只停留在初见 没有后来的鸡毛蒜皮 没有日久生厌 如此初见 才可以留下永恒初恋的甜蜜与心跳
    心玩家正信阅读 234评论 0 0