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对象了。

参考文章

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

读后感

可以愉快的装逼了
转载文章作者:小鳄鱼的大哥哦 https://www.jianshu.com/p/9f0ffb551309
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值