一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 前言
三 同源策略
四 实现跨域的方式
4.1 JSONP
4.2 CORS
4.3 postMessage
4.4 WebSocket
4.5 Node
4.6 Nginx
4.7 其他方式
五 参考文献

二 前言

返回目录

带着问题学习:

  • 什么是跨域?
  • 为什么要设置跨域?
  • 如何解决跨域?

三 同源策略

返回目录

【1】什么是同源策略

所谓 ,可以指 URL。

简单来看某个 URL 组成;

在这里:

名称举例
协议httphttps
域名github.comjsliang.top
端口80443
其中,如果 URL 上未标明端口,那么 http 默认是 80 端口,https 默认是 443 端口。

而所谓的同源策略,是指这 3 个(协议、域名、端口)一致的情况下,才属于同源。

对于下面的 URL,我们判断下哪些属于同源哪些属于不同源:

URL是否同源原因
http://github.com协议不同
https://github2.com域名不同
https://github.com:80/LiangJunronghttps 默认端口为 443
https://money.github.com多级域名和主域名一致

在上面,如果两个页面对应的地址不同源,那么浏览器就会判定跨域,从而导致下面问题:

  • Ajax 请求不能发送
  • 无法获取 DOM 元素并进行操作
  • 无法读取 CookieLocalStorageIndexDB

那么,出于怎样的考虑,【2】浏览器才要设置跨域

首先,跨域只存在于浏览器端,因为我们知道浏览器的形态是很开放的,所以我们需要对它有所限制。

其次,同源策略主要是为了保证用户信息的安全,可分为两种:Ajax 同源策略和 DOM 同源策略。

Ajax 同源策略主要是使得不同源的页面不能获取 Cookie 且不能发起 Ajax 请求,这样在一定层度上防止了 CSRF 攻击。

DOM 同源策略也一样,它限制了不同源页面不能获取 DOM,这样可以防止一些恶意网站在自己的网站中利用 iframe 嵌入正规的网站并迷惑用户,以此来达到窃取用户信息。

【3】实际开发场景为什么会出现跨域

  1. 前后端部署的机子,不属于同一台云服务器。
  2. 同一台云服务器,但是你在 https://github.com 请求的是 https://github2.com 上的资源。

那么,【4】如何解决跨域问题呢?

  1. 使用代理(proxy
  2. 设置 CORS
  3. JSONP
  4. ……等

下面我们详细看看解决跨域的几个方案。

四 实现跨域的方式

返回目录

4.1 JSONP

返回目录

利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP 请求一定需要对方的服务器做支持才可以。

优缺点:

  1. 【优点】AjaxJSONP 都是客户端向服务端发送请求,从而获取数据的方式。但是 Ajax 属于同源策略,而 JSONP 属于非同源策略。
  2. 【优点】兼容性好,能解决主流浏览器跨域访问的问题。
  3. 【缺点】仅支持 get 请求
  4. 【缺点】不安全,可能会遭受 XSS 攻击。

实现过程:省略,看 JS 手写代码部分。

4.2 CORS

返回目录

CORS 跨域的原理

跨域资源共享(CORS)是一种机制,是 W3C 标准。它允许浏览器向跨源服务器,发出 XMLHttpRequestFetch 请求。并且整个 CORS 通信过程都是浏览器自动完成的,不需要用户参与。

而使用这种跨域资源共享的前提是,浏览器必须支持这个功能,并且服务器端也必须同意这种 "跨域" 请求。因此实现 CORS 的关键是服务器需要服务器。

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS

该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

CORS 的请求分为两种:

  • 简单请求
  • 复杂请求

一个简单请求大致如下。

HTTP 方法是下列之一:

  • HEAD
  • GET
  • POST

HTTP 头信息不超过以下几种字段

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • ……等(记不住。。。)

任何不满足上述要求的请求,都是复杂请求。

一个复杂请求不仅包含通讯内容的请求,同时也包含预请求。

简单请求和复杂请求的区别

  • 简单请求 的发送从代码上看起来和普通的 XHR 没太大区别,但是 HTTP 头当中要求总是包含一个域(Origin)的信息。该域包含协议名、地址以及一个可选的端口。
  • 复杂请求 不止发送一个请求。其中最先发送的是一种 “预请求”,而服务端也需要返回 “预回应” 作为相应。预请求实际上是对服务端的一种权限请求,只有当预请求成功返回,实际请求才开始执行。

4.3 postMessage

返回目录

postMessageHTML5 XMLHttpRequest Level 2 中的 API,且是为数不多可以跨域操作的 window 属性之一。

它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的 iframe 消息传递
  • 上面三个场景的跨域数据传递

postMessage() 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

4.4 WebSocket

返回目录

WebSocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。

WebSocket 是一种双向通信协议,在建立连接之后,WebSocketserverclient 都能主动向对方发送或接收数据。

WebSocket 的使用我们在计算机网络部分有写,后面再进行介绍。

4.5 Node

返回目录

同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。

所以我们可以通过 Node 中间件代码(两次跨域):

  • 接受客户端请求
  • 将请求转发给服务器
  • 拿到服务器响应数据
  • 将响应转发给客户端

4.6 Nginx

返回目录

使用 Nginx 反向代理实现跨域,是最简单的跨域方式。

只需要修改 Nginx 的配置即可解决跨域问题,支持所有浏览器,支持 Session,不需要修改任何代码,并且不会影响服务器性能。

4.7 其他方式

返回目录

其他方式还有:

  1. window.name + iframe
  2. location.hash + iframe
  3. document.domain + iframe
  4. ……

但是感觉有生之年 jsliang 不会用到,这里看看即可。

五 参考文献

返回目录

jsliang 的文档库由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/>本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。

jsliang
393 声望31 粉丝

一个充满探索欲,喜欢折腾,乐于扩展自己知识面的终身学习斜杠程序员