必须搞懂的跨域解决方案

简介: 必须搞懂的跨域解决方案

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

跨域的前置知识:同源策略

关于跨域是什么,我们这里先不做介绍,我们先介绍下跨域的前置知识(同源策略)。同源策略是浏览器中非常重要的安全策略,用于限制不同源的文档或它加载的脚本,对其他文档的访问,帮助阻拦恶意文档,减少可能被攻击的媒介。

同源的定义

判断两个URL是否同源,主要判断是协议、主机、端口号,三者是否一致,只有这三者都相同,才是同源。

image.png

IE浏览器中同源策略的差异

IE浏览器中的同源策略和其他浏览器中的同源策略主要有以下两个差异点:

  1. 授信范围:IE浏览器认为如果两个URL是高度互信的域名,如公司域名,则不受同源策略的限制。
  2. 端口:IE浏览器未将端口号纳入到同源策略的检查中,因此即使端口号不同,只要主机和协议相同,也是属于同源的。

跨域网络访问的类型

同源策略控制不同源之间的交互,这些交互可以分为以下三类:

跨域写操作

跨域写操作一般是允许的,例如链接(links)、重定向以及表单提交。

跨域读操作

一般是不被允许的,但是我们可以通过内嵌资源来巧妙地进行读取访问。

跨域资源嵌入

这种类型一般是被允许地,主要有以下实例:

  • script标签
  • link标签
  • img标签
  • video和audio播放地多媒体资源
  • 通过<object><embed><applet>嵌入的插件。
  • 通过@font-face引入的字体。
  • 通过<iframe>载入的任何资源。

跨域请求有没有发送到服务器端?

跨域请求实际上已经发送到了服务器,并且客户端也接收到了返回的消息,然而浏览器在接收消息后发现这个信息违反了同源策略且没有被允许跨域,所以在解析该消息的时候会报错。

同源策略限制哪些,不限制哪些?

限制以下内容:

一般为跨域读操作。

  • Ajax请求。
  • Cookie、LoaclStorage。
  • DOM对象。

不限制以下内容:

不限制的内容主要是上文的跨域资源嵌入部分。

跨域解决方案

方案一:JSONP

核心思路:利用html中的script标签不受同源策略的限制来进行跨域,在客户端脚本中定义好处理的函数,然后通过请求参数传递给服务器端,服务器端进行字符串拼接后返回调用该函数。

  • 客户端


image.png

image.png

CodeSandBox在线演示

优缺点

  • 优点:兼容IE。
  • 缺点:仅支持get方法,且需要服务器端进行协同。由于是script标签,所以读不到ajax那么精确的状态,不知道状态码是什么,也不知道响应头是什么。

JSONP带来的安全风险

使用JSONP跨域可能会带来JSONP劫持的问题,这个问题属于CSRF攻击范畴,当某网站通过JSONP的方式来实现跨域并传递给用户认证的敏感信息后,攻击者可以构造恶意的JSONP调用页面,诱导被攻击者访问来达到截取用户敏感信息的目的。

关于JSONP劫持漏洞攻击可以看下面的这篇文章。

JSONP 劫持原理与挖掘方法

JSONP漏洞利用的原理

下面以一个实例为例介绍什么是JSONP劫持。

  1. 假设用户已经在网站B上注册并进行了登录,网站B包含了用户的id,name,email等敏感信息。
  2. 此时有一个恶意网站A,用户通过浏览器向网站A发送URL请求。
  3. 网站A向用户返回响应界面,这个响应的页面中包含了一个JS函数和向网站B请求的script标签。(script标签中的内容如下所示)

  1. 用户收到响应后,解析JS代码,将回调函数作为参数向网站B发送请求。
  2. 网站B收到请求后,解析请求的URL,以JSON格式生成请求需要的数据,将封装的包含用户信息的JSON数据作为回调函数返回给浏览器。
  3. 网站B数据返回后,浏览器自动执行callback函数对步骤4返回的JSON格式的数据进行处理,此时就可能将数据传回给网站A的服务器,这样网站A利用网站B的JSONP漏洞便获得了用户在网站B注册的信息。

方案二:CORS

CORS(跨域资源共享)会允许服务端来指定哪些主机可以从这个服务端加载资源。CORS通过HTTP头的形式告诉浏览器哪些不同来源的客户端可以访问本站的资源的。实现CORS跨域的关键在服务端,只要服务端设置了Access-Control-Allow-Origin就可以开启CORS,客户端发送请求时请求头加origin,服务器返回的响应头加Access-control-allow-origin,浏览器以此来判断是否允许跨域。该属性可以设置哪些域名可以访问服务器,如果设置为星号则表示所有资源都可以访问服务器资源。CORS进行跨域的时候会将请求分为简单请求和预检请求。

1. 简单请求

只要同时满足下面的两个条件,就可以判断为简单请求,简单请求只需在请求时加上origin字段,响应时包含Access-Control-Allow-Origin字段,浏览器以此来判断是否允许跨域。

  1. 请求方法是以下三种方法之一:
  • HEAD
  • GET
  • POST
  1. HTTP的头信息Request Headers不超出以下几种字段
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type: (只限于下面的三个值)
  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

2. 预检请求

不满足简单请求条件的,则判断为需要进行预检请求,浏览器首先使用OPTIONS方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。预检请求的使用可以避免未获得许可的调用方,调用了有副作用的API对服务器端的数据进行修改。

预检请求包含的字段
  • OPTIONS
  • Origin
  • Access-Control-Request-Method
  • Access-Control-Request-Headers
预检请求的响应字段

上面的字段告知服务器,实际请求将采用什么方法,将包含什么样的请求头。预检请求完成之后,将发送实际请求。

CORS跨域的优缺点

  • 优点
  • CORS通信与同源的AJAX通信没有差别,代码容易维护。
  • 支持所有类型的HTTP请求
  • 缺点
  • 存在兼容性问题,特别是IE10以下的浏览器
  • 第一次发送非简单请求时会多一次预检请求(第一次之后,服务器对预检请求的响应有一个有效时间)

CORS跨域实例

  • 客户端

  • 服务器端

注意:CORS请求如果需要携带cookie信息的时候,需要将withCredentials置为true。

JSONP和CORS比较

  • JSONP只支持GET请求,CORS支持所有类型的HTTP请求。
  • JSONP比CORS的兼容性好。

方案三:Nginx

在介绍什么是Nginx跨域之前,我们首先来系统性的介绍下什么是反向代理。

反向代理和正向代理

反向代理指的是隐藏了真实的服务端,举个例子,当我们请求百度的时候,背后可能有成千上万台服务器为我们服务,但具体是哪一台,我们并不知道,我们只需要知道我们的反向代理服务器是百度即可,反向代理会帮我们把请求转发到真实的服务器那里去,Nginx就是一种反向代理服务器。顺便提一下,正向代理指的是隐藏了真实的客户端,比如我们如果想要通过代理访问谷歌,此时的代理就是正向代理,因为此时隐藏了真实的客户端。

Nginx跨域的原理

Nginx作为反向代理服务器,就是把客户端的HTTP请求转发到另一个服务器上,代理服务器访问另一个服务器是不存在跨域问题的,nginx代理服务器获取到数据后,再转发给客户端即可实现跨域。

  • 浏览器角度

从浏览器的角度看,就像是访问同源服务器上的URL。

  • 服务器角度

从服务器角度看,并不知道这个请求是来自代理服务器的,简单来说是nginx服务器欺骗了浏览器,让浏览器认为是同源调用,又通过重写url,欺骗了真实的服务器,让他以为这个HTTP请求是直接来自用户浏览器的,这样就解决了跨域问题。

方案四:iframe + postMessage

postMessage是HTML5新增的一项功能,该方法提供了一种受控机制来规避同源策略,可以实现跨源通信。

实现原理

父页面中通过iframe标签引入子页面,主页面加载完毕之后,通过postMessage方法向子页面发送消息,同时监听来自子页面的消息。子页面通过window.addEventListener方法监听来自父页面的消息,并通过top.postMessage将消息传递给父页面,从而实现跨域通信。

下面是实现的效果图

方案五:window.name + iframe

跨域原理

核心原理:在一个窗口window的生命周期内,窗口载入的所有页面共享一个window.name,每个页面对window.name都有读写权限,window.name持久存在一个窗口载入过的所有页面中。。

实现流程

假如A页面要跨域访问B页面,下面介绍下如何实现。

  1. 在页面A中创建一个iframe,将其src指向要跨域的页面B,此时页面B中的window.name中存储着数据,A页面中的iframe此时也能够拿到这个数据。
  2. 在A页面第一次调用onload事件的时候,将其src属性改为本地域的一个代理html,这个文件可以是一个空文件,修改src属性后,onload会被再次触发,但是此时window.name还是可以获取到的。
  3. 获取数据后,为了防止其他iframe获取这个数据,需要销毁这个iframe。

在线实现

方案六:WebSocket跨域

WebSocket协议是HTML5一种新的协议,实现了浏览器与服务器的全双工通信,同时允许跨域通讯,允许服务器端主动向浏览器端发送消息。WebSocket和HTTP都是应用层协议,都基于TCP协议,但是WebSocket在建立的时候需要借助HTTP协议,连接建立好之后,客户端和服务器端之间的双向通信就与HTTP无关了。

WebSocket跨域原理

客户端可以通过new WebSocket创建一个socket实例,然后通过onopen方法中的send方法将要发送的数据传到后端,也可以利用onmessage方法来监听服务端发送过来的消息,服务端是首先引入ws模块,然后通过new WebSocket.Server来监听一个端口,然后利用message接收数据,利用send向客户端发送数据。

实例分析

客户端是建立在5500端口的,服务端是在5000端口的,这样可以测试能否跨域。

  • 客户端实现
<script>
    let socket = new WebSocket('ws://localhost:5000');
    socket.onopen = function() {
        socket.send('服务端你好');
    }
    socket.onmessage = function(e) {
        console.log('服务端发过来的消息为:',e.data);
    }
</script>
复制代码
  • 服务端实现
let express = require('express');
let app = express();
let webSocket = require('ws');
let wss = new webSocket.Server({port:5000});
wss.on('connection',function(ws) {
    ws.on('message',function(data) {
        console.log('客户端发过来的消息为:',data);
        ws.send('客户端你好');
    })
})
console.log("http://localhost:5000");
复制代码

方案七:NodeJs中间件代理跨域

node中间件实现跨域,原理大致和nginx跨域类似,都是通过一个代理服务器,实现数据的转发,类似的中间件有http-proxy-middleware。

服务端8000端口上有客户端想要获取的数据,客户端在5500端口上,代理服务器在3000端口上。

  • 客户端代码
<script>
    fetch('http://localhost:3000/api', {
        method: 'GET'
    }).then(value => { console.log(value); })
</script>
复制代码
  • 代理服务器端代码
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use('/api', createProxyMiddleware({
    // 客户端想要访问的跨域目标地址
    target: "http://localhost:8000",
    // 可以让参数是域名
    changeOrigin: true,
    pathRewrite: {
        '^/api': '',
    }
}))
// 主动监听3000端口
app.listen(3000);
console.log('代理服务器运行在:http://localhost:3000');
复制代码
  • 目标跨域页面代码
<body>
    <h1>这是用户目标想要跨域的页面</h1>
</body>
复制代码

其余跨域方案

参考资料


相关实践学习
基于函数计算快速搭建Hexo博客系统
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
4天前
|
JSON 前端开发 安全
前端开发中的跨域解决方案探究
跨域是前端开发中常见的问题之一,本文将探讨跨域的概念、产生的原因,以及常见的解决方案,包括JSONP、CORS、代理等。通过本文的学习,读者可以深入了解跨域问题及解决方案,为自己的前端开发工作提供参考。
|
3天前
|
存储 安全 前端开发
第五章 跨域资源共享(CORS):现代Web开发中的关键机制
第五章 跨域资源共享(CORS):现代Web开发中的关键机制
|
4天前
|
Web App开发 JavaScript 前端开发
深入理解前端跨域方法和原理
深入理解前端跨域方法和原理
|
4天前
|
JSON JavaScript 前端开发
跨域的原理及解决方案
同源策略是Web应用程序安全性模型中的重要概念。根据该策略,Web浏览器允许第一个网页中包含的脚本访问第二个网页中的数据,但前提是两个网页具有相同的来源。来源由URI,主机名和端口号的组合定义。此策略可防止一个页面上的恶意脚本通过该页面的DOM(Document Object Model)获得对另一网页上敏感数据的访问。 JSONP 由于同源策略,一般来说位于server1.example.com...
|
9月前
|
前端开发 Java 应用服务中间件
项目里面怎么解决跨域的?
项目里解决跨域的方法
|
11月前
|
网络协议 Java 数据库连接
java后端跨域 配置 代码
java后端跨域 配置 代码
100 0
|
移动开发 JavaScript 前端开发
|
JSON 前端开发 安全
【实战晋级】理解跨域以及工作中跨域问题的处理 - 1
相信大部分前端工程师在日常工作中经常使用 xhr 或者 fetch 从后端 api 里取数据然后进行二次处理,随后渲染到页面。
117 0
【实战晋级】理解跨域以及工作中跨域问题的处理 - 1
|
前端开发 文件存储 数据安全/隐私保护
【小刘带你玩儿前端】什么是跨域以及如何解决?
现在的web项目,很多都是前后端分离,特别容易出现跨域问题
【小刘带你玩儿前端】什么是跨域以及如何解决?
|
前端开发 数据库 开发者
学习跨域的必要性| 学习笔记
快速学习学习跨域的必要性。
65 0