文章目录
浏览器的跨域是什么?解决方案是什么?
面试题
- 什么是跨域,跨域是谁引起的?为什么服务端没有同源限制?
- 什么是同源策略,为什么有同源策略?
- CORS的相关请求头
- 手写JSONP
- 简单请求和非简单请求
- options请求是什么?有什么作用?
同源策略
同源策略是浏览器的一种安全策略
同源策略:两个 URL 的协议、域名和端口都相同。
注意: 两个不同的域名指向同一个ip地址,也非同源。
浏览器默认两个相同的源之间是可以相互访问资源和操作 DOM 的。
同源政策主要限制了三个方面
- 当前域下的 js 脚本不能访问其他域下的 cookie、localStorage
- 当前域下的 js 脚本不能操作访问操作其他域下的 DOM
- 当前域下 ajax(XMLHttpRequest)等方式 无法发送跨域请求。
但是有三个标签是允许跨域加载资源:
<img src=XXX>
<link href=XXX>
<script src=XXX>
跨域问题
跨域问题的描述
违背了同源策略就是跨域
同源策略是浏览器的行为。当客户端向服务器请求数据,服务器返回数据给浏览器后,浏览器发现跨域(不是同源),不将数据给客户端
跨域问题的解决
- Cross-Origin Resource Sharing CORS跨域资源共享
- 代理服务器,代理服务器和客户端同源。服务器之间没有同源策略
- JSONP 就是利用
<script>
标签没有跨域限制的漏洞 - 跨文档消息机制,可以通过 window.postMessage 的 JavaScript 接口和不同源的 DOM 进行通信。
JSONP
JSONP (JSON with Padding)的原理很简单: 就是利用 <script>
标签没有跨域限制的漏洞。
主要实现
动态插入script标签,通过script标签引入一个js文件。
前端先定义一个回调函数,其参数是需要接受的数据。将该回调函数作为url的参数传给后端。告诉后端回调函数的名字。
后端将数据作为回调函数的参数传给浏览器。
浏览器执行后端返回的立即执行函数(前端定义的回调函数),获取到数据。
1.前端设置一个函数,动态创建一个 script 标签,通过callback参数告诉后端回调函数名叫handle
//1.动态创建 script
var script = document.createElement('script');
//2.处理数据的回调函数
function handle(data){
//获取result元素
console.log(data);
}
//3.设置 script 的 src 属性,告诉服务器回调函数是handle
script.src = 'http://localhost:3000/?callback=handle';
// 让 script 生效
document.body.appendChild(script);
2.需要后端配合,后端能够识别这样的 URL 格式并处理该请求,然后返回handle({"name": "ranan"})
给浏览器。
const data = {
name:'ranan'
}
handle(data)
3.浏览器在接收到 handle({"name": "ranan"})
之后立即执行 handle方法(前端定义的),获得后端返回的数据,这样就完成一次跨域请求了。
JSONP 特点
- 使用简单且兼容性不错,但是只限于 get 请求。
- JSONP 需要后端配合返回指定格式的数据。
- 由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。
CORS 跨域资源共享
是什么?
浏览器提供了CORS 跨域资源共享机制,使用该机制可以进行跨域访问控制。 —> 浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。
浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉
优点与缺点
优点
- CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
- 支持所有类型的 HTTP 请求。
缺点
- 第一次发送非简单请求时会多一次请求。
简单请求与非简单请求
简单请求
1.请求方法是以下三种方法之一:
- HEAD
- GET
- POST
2.HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
同时满足以上两个条件就是简单请求。
简单请求的流程
1.在请求中需要附加一个额外的 Origin
头部,包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。例如:Origin: http://www.xxx.cn
2.如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin
中回发相同的源信息(如果是公共资源,可以回发 * )。
3.没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。
非简单请求
1.浏览器会在正式通信之前,增加一次HTTP查询请求。使用 OPTIONS 方法发起一个预检(preflight)请求到服务器。
目的是为了知道服务器是否允许该请求。
服务器成功响应预检请求后,浏览器才会发送真正的请求,并携带真实数据
- Origin:与简单的请求相同。
- Access-Control-Request-Method: 请求自身使用的方法。
- Access-Control-Request-Headers: (可选)自定义的头部信息,多个头部以逗号分隔
2.服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通:
- Access-Control-Allow-Origin:与简单的请求相同。
- Access-Control-Allow-Methods: 允许的方法,多个方法以逗号分隔。
- Access-Control-Allow-Headers: 允许的头部,多个方法以逗号分隔。
- Access-Control-Max-Age: 将这个 Preflifght 请求缓存多长时间(以秒表示)。
一旦服务器通过 Preflight 请求允许该请求之后,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样了。
代理服务器 利用脚手架配置
代理服务器和客户端同源,两个相同的源之间是可以相互访问资源和操作 DOM 的。
服务器和服务器之间没有同源策略。
修改vue.config.js
module.exports = {
devServer:{
proxy:{//遇见xxx前缀的请求,走代理去服务器端获取
'/xxx':{
target:'http:localhost:5000', //需要将请求转发到服务器的地址
}
}
}
H5新特性 postMessage
跨文档消息机制:通过使用window.postMessage(message,targetOrigin)
和不同源的 DOM 进行通信。
发送方通过 postMessage API 来发送消息,而接收方可以通过监听 message 事件,来添加消息处理回调函数
iframe+postMessage
1.父窗口给子窗口发送消息的方式:
document.getElementById('myframe').contentWindow.postMessage('MessageFromIndex1','*');
其实就是在父窗口中操作子窗口发消息,然后让子窗口接收自己刚才发的消息。
2.子窗口给父窗口发送消息的方式:
window.parent.postMessage( {msg: 'MessageFromIframePage'}, '*');
注意:此处parent === window.parent,即子窗口的父窗口
其实就是在子窗口中操作父窗口发消息,然后让父窗口接收自己刚才发的消息。
总结:所谓的跨窗口发送消息,就是通过在别的窗口操作本窗口发送消息,然后本窗口再自己接收的方式实现。
iframe+postMessage跨域的具体实现
1.父窗口创建跨域iframe子窗口,子窗口的src就是目标通信的地址
2.父窗口给子窗口发送消息