前端
network
浏览器跨域

浏览器跨域

什么是跨域

跨域问题的来源是浏览器为了请求安全而引入的基于同源策略的安全特性。当页面和请求的协议主机名端口不同时,浏览器判定两者不同源,即为跨域请求。需要注意的是跨域是浏览器的限制,服务端并不受此影响。当产生跨域时,我们可以通过 JSONP、CORS、postMessage 等方式解决。

如何判定跨域

比如一个域名是:https://example.com:443 (opens in a new tab)

则,https协议example.com主机名443端口 这三块也是同源策略的判定条件,只有当协议、主机名和端口都相同时,浏览器才判定两者是同源关系,否则即为跨域。

跨域的解决方案

前端常见的跨域解决方案有 CORS、反向代理(Reverse Proxy)、JSONP 等。

CORS (Cross-Origin Resource Sharing)

CORS 是目前最为广泛的解决跨域问题的方案。方案依赖服务端/后端在响应头中添加 Access-Control-Allow-* 头,告知浏览器端通过此请求。

涉及到的端:

CORS 只需要服务端/后端支持即可,不涉及前端改动。

具体实现方式:

CORS 将请求分为简单请求(Simple Requests)和需预检请求(Preflighted requests),不同场景有不同的行为:

简单请求

不会触发预检请求的称为简单请求。当请求满足以下条件时就是一个简单请求:

  • 请求方法:GETHEADPOST
  • 请求头:AcceptAccept-LanguageContent-LanguageContent-Type
    • Content-Type 仅支持:application/x-www-form-urlencodedmultipart/form-datatext/plain

需预检请求

当一个请求不满足以上简单请求的条件时,浏览器会自动向服务端发送一个 OPTIONS 请求,通过服务端返回的 Access-Control-Allow-* 判定请求是否被允许。

CORS 引入了以下几个以 Access-Control-Allow-* 开头:

  • Access-Control-Allow-Origin 表示允许的来源
  • Access-Control-Allow-Methods 表示允许的请求方法
  • Access-Control-Allow-Headers 表示允许的请求头
  • Access-Control-Allow-Credentials 表示允许携带认证信息

当请求符合响应头的这些条件时,浏览器才会发送并响应正式的请求。

反向代理

反向代理解决跨域问题的方案依赖同源的服务端对请求做一个转发处理,将请求从跨域请求转换成同源请求。

涉及到的端

反向代理只需要服务端/后端支持,几乎不涉及前端改动,只用切换接口即可。

具体实现方式

反向代理的实现方式为在页面同域下配置一套反向代理服务,页面请求同域的服务端,服务端请求上游的实际的服务端,之后将结果返回给前端。

JSONP

JSONP 是一个相对古老的跨域解决方案,只支持 GET 请求。主要是利用了浏览器加载 JavaScript 资源文件时不受同源策略的限制而实现跨域获取数据。

涉及到的端

JSONP 需要服务端和前端配合实现。

具体实现方式

  • 创建 <script> 标签:在网页中动态创建一个 <script> 标签。
  • 请求数据:将目标服务器的 URL 设置为 <script> 标签的 src 属性。
  • 回调函数:服务器返回的数据不是纯粹的 JSON,而是一个函数调用,并且这个函数是在客户端预先定义好的。服务器返回的数据格式通常是这样的:callbackFunction({"key": "value"});
  • 执行回调:当 <script> 加载完成后,返回的 JavaScript 代码会被执行,调用预先定义好的回调函数,并将数据传递给它。

非常用方式

  • postMessage
    • 即在两个 origin 下分别部署一套页面 A 与 B,A 页面通过 iframe 加载 B 页面并监听消息,B 页面发送消息。
  • window.name
    • 主要是利用 window.name 页面跳转不改变的特性实现跨域,即 iframe 加载一个跨域页面,设置 window.name,跳转到同域页面,可以通过 $('iframe').contentWindow.name 拿到跨域页面的数据。
  • document.domain
    • 可将相同一级域名下的子域名页面的 document.domain 设置为一级域名实现跨域。
    • 可将同域不同端口的 document.domain 设置为同域名实现跨域(端口被置为 null)。

参考