解决跨越的几种方案

简介: 跨域产生的原因首先是受浏览器的安全性设计影响,由于浏览器同源策略的设计,所以产生了跨域。

跨域产生的原因首先是受浏览器的安全性设计影响,由于浏览器同源策略的设计,所以产生了跨域。


在项目中,我们常常遇到跨域的问题,虽然在你的项目里,脚手架已经100%做好了本地代理、或者运维老铁在nginx中也已经给你做了接口代理,所以你遇到跨域的概率会少了很多,但是在传统的项目中,在那个jquery的横行时代,或者你接手了一个祖传项目时,跨域问题会是时有发生,本文只做笔者了解跨域的通用解决方案,希望在实际项目中对你有些思考和帮助。


正文开始...


何为同源


  • 协议相同
  • 域名相同
  • 端口相同


非同源是无法彼此访问cookie,dom操作ajaxlocalStorageindexDB操作


但是非同源可以访问以下一些属性

window.postMessage();
window.location
window.frames
window.parent
...

对于非同源网站跨域通信,我们可以采用以下几种方案


片段标识符


通过在url上放入#type,利用hashchange事件,获取父页面的相关hash数据

// parent
const type = 'list';
const originUrl = originUrl + '#'+type;
const iframe = document.getElementById('iframe');
iframe.src = originUrl;

在子iframe中,我们可以监听hashchange事件

// iframe
window.addEventListener('hashchange', (evt) => {
  console.log(evt) // 获取片段标识符的相关数据
})


跨文档通信 window.postMessage


子页面可以利用window.postMessage向父页面发送信息,父页面监听message接收子页面发送的消息,通常这种方式在iframe通信中用得比较多,也是跨域通信的一种解决方案

// parent
window.addEventListener('message', (evt) => {
  const data = evt.data;
  console.log(data)
})
// child
const origin = '*'; // 这里一般是父页面的域名,但是也可以是*
const data = {
  text: 'hello, world'
};
window.postMessage(data,origin)


jsonp


利用script标签,向服务端发送一个get请求,url上绑定一个callback=fn,这个fn通常是与后端约束好,fn是客户端执行的函数名。


用一个简单的例子来解释下jsonp


客户端示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>jsonp</title>
</head>
<body>
    <h3>jsonp</h3>
    <div id="app"></div>
    <script>
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.src = 'http://192.168.31.40:8080/api?callback=jsonp';
        document.body.appendChild(script);
        const renderHtml = ({name, age}, dom) => {
            dom.innerHTML = `<div>我的名字 ${name},今年 ${age}岁了</div>`
      }
      function jsonp(data) {
          console.log(data);
          const app = document.getElementById('app');
          const {name, age} = data;
          renderHtml({name, age}, app)
        }
    </script>
</body>
</html>

服务端示例代码

// server.js
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = '8080';
const server = http.createServer((req, res) => {
    res.statusCode = 200;
    // console.log(req.url);
    res.setHeader('Content-Type', 'text/html');
    fs.readFile(path.resolve(__dirname, '../', 'index.html'), (err, data) => {
        if (err) {
          res.end('404');
          return;
        }
        res.end(data);
    });
    const data = {
      name: 'Maic',
      age: Math.floor(Math.random() * 20)
    }
    if (req.url.includes('/api')) {
        res.end(`jsonp(${JSON.stringify(data)})`)
    }
});
server.listen(PORT, () => {
   console.log('server is start'+ PORT);
})

注意我们看到有这样的一段判断req.url.includes('/api'),然后我们res.end(jsonp(JSON.stringify(data))),返回的就是jsonp这个回调函数,把数据当形参传给前端,在客户端定义的jsonp方法就能获取对应的数据。


执行node server.js打开http://localhost:8080我们会发现

12837a06efa3aa4cc80134ab435a9cb1.png

返回的数据就是jsonp({name: 'xx',age: 'xx'})


我们看下请求头

1abc4817cd4ba79c50b593e011fb37ec.png

浏览器会发送一个get请求,所携带的参数就是callback: jsonp,而我们在客户端确实是通过jsonp这个方法拿到对应的数据了。


所以我们可以知道jsonp实际上就是利用一个客户端发送的get请求携带一个后端服务的返回的回调函数,在客户端,我们定义这个回调函数就可以获取后端返回的形参数据了。


jsonp这种跨域通信来看,其实有也它的缺点和优点


  • 缺点


1、它的安全性会有一定风险,因为依赖的结果就是那个回调函数的形参内容,如果被人劫持修改返回数据,那可能会造成安全性问题


2、仅支持get请求,不支持其它http请求方式,我们发现jsonp这种通信就是利用script标签请求了一个url,url上携带了一个可执行的回调函数,进而通过后端给回调函数传递数据的。


3、没有任何状态码,数据丢给客户端,如果有失败情况,不会有像http状态码一样


  • 优点


能解决跨域通信问题,兼容性比较好,不受同源策略的影响,对后端来说实现也简单。

在以前比较久远的项目中,你可能是直接使用jqueryjsonpok了,大概的代码就是长这样的

$.ajax({
  url: 'xxx',
  dataType: 'jsonp',
  jsonp: 'successCallback',
  success: (data) => {
    console.log(data)
  },
  error: (err) => {
    console.log(err)
  }
})

successCallback这个就是与服务端约束的回调函数,一般与服务端沟通一致就行,那么简单的jsonp就已经完成了,是不是感觉很简单呢?


WebSocket


由于WebSocket不受同源策略的限制,因此WebSocket也是可以实现跨域通信的。这里就不举例子了,具体可以参考之前写的一篇浅谈websocket这篇文章


CORS跨域资源分享


这种方式是在服务端进行控制,允许任何资源请求。其实在浏览器端,即使跨域,还是会正常请求,只是请求非同源环境的后端服务,浏览器禁止请求访问,更多可以参考这篇文章cors[1]


我们写个例子具体测试一下,在客户端加入这段代码

const send = document.getElementById('send');
  send.onclick = function () {
        if (!window.fetch) {
          return;
        }
       fetch('http://localhost:8081/list.json')
          .then((res) => res.json())
          .then((result) => {
           console.log(result);
            const contentDom = document.querySelector('.content');
            renderHtml(result, contentDom);
          });
      };

服务端代码,我们新建一个index2.js服务端代码,并执行node index2.js

const http = require('http');
const PORT = '8081';
const server = http.createServer((req, res) => {
    res.statusCode = 200;
    // // console.log(req.url);
    res.setHeader('Content-Type', 'application/json');
    if (req.url.includes('/list.json')) {
      res.end(JSON.stringify({
        name: 'maic',
        age: Math.ceil(Math.random() * 20)
      }));
    }
});
server.listen(PORT, () => {
   console.log('server is start'+ PORT);
})

我们注意到请求的端口是8081,打开8080页面

ea677d551e15f63e69a51a55f6bb474c.png

点击按钮,发送fetch请求,我们发现浏览器报了这样你常常看到的跨域信息as been blocked by CORS policy: No 'Access-Control-Allow-Origin' header...,因为跨域了


紧接着我们在服务端设置下Access-Control-Allow-Origin: *

const http = require('http');
const PORT = '8081';
const server = http.createServer((req, res) => {
    res.statusCode = 200;
    // // console.log(req.url);
    res.setHeader('Content-Type', 'application/json');
    res.setHeader('Access-Control-Allow-Origin', '*');
    if (req.url.includes('/list.json')) {
      res.end(JSON.stringify({
        name: 'maic',
        age: Math.ceil(Math.random() * 20)
      }));
    }
});
server.listen(PORT, () => {
   console.log('server is start'+ PORT);
})

此时再次访问时,已经ok了

c3fa3209bd5fd3ad19775d524f4a8468.png

12b368c7c5b850517fc824326342cb1f.png

注意我们可以看下Response Headers

HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *
Date: Sun, 01 May 2022 14:23:59 GMT
Connection: keep-alive
Content-Length: 24

Access-Control-Allow-Origin:*这是我们服务的设置响应请求头,设置允许所有域名请求。


因此cors跨域其实在服务端设置Access-Control-Allow-Origin:*也就完美的解决了跨域问题。


总结


  • 跨域产生的原因,主要受同源策略的影响,非同源环境,无法相互访问cookielocalStoragedom操作


  • 解决跨域的方案主要有片段标识符iframe通信postMessage,jsonpWebSocketcors


  • 用具体实际例子深入了解几种跨域模式,比如jsonp,实际上是利用script发送一个get请求,在get请求的参数中传入一个可执行的回调函数,服务根据请求,将返回一个前端可执行的回调函数,并且将数据当成该回调函数的形参,在前端定义该回调函数,从而获取调函数传入的数据。


  • 用具体例子服务端设置cors,主要是在后端接口返回响应头里设置Access-Control-Allow-Origin:*允许所有不同源网站访问,这种方法也是比较粗暴的解决跨域问题的常用手段。


  • 本文示例代码code example[2]
相关文章
|
存储 边缘计算 运维
核心网架构 | 带你读《5G时代的承载网》之十三
对 5G 核心网进行了颠覆性的设计,通过基于服务的架构、网络切片、C/U 分离等,结合云化技术,实现网络的定制化、开放化、服务化,支持大流量、 大连接和低时延的万物互联需求。
核心网架构  | 带你读《5G时代的承载网》之十三
|
20天前
|
存储 缓存 负载均衡
跨越界限:前端与后端的协同优化
在当今快节奏的技术环境下,前端与后端的协同优化成为了提升应用性能和用户体验的关键。本文探讨了如何通过前后端的紧密合作,实现更高效的数据传输、渲染优化以及系统性能提升,从而构建出更加流畅、响应迅速的应用程序。
|
4月前
|
前端开发
微前端架构:跨越边界的独立协作
在传统的前端开发中,单体应用往往面临代码复杂、维护困难等问题。而微前端架构通过将应用拆分成独立的小型服务,实现了不同团队的独立开发与部署,从而提升了开发效率和可维护性。本文将介绍微前端架构的概念与实践,探讨其在实际项目中的应用场景和优势。
|
12月前
|
5G SDN 网络架构
带你读《5G 系统技术原理与实现》——1.2.1 5G 移动通信系统整体网络架构
带你读《5G 系统技术原理与实现》——1.2.1 5G 移动通信系统整体网络架构
|
12月前
|
人工智能 运维 安全
带你读《智慧光网络:关键技术、应用实践和未来演进》——1.5.3 智慧光网络的目标
带你读《智慧光网络:关键技术、应用实践和未来演进》——1.5.3 智慧光网络的目标
|
存储 消息中间件 监控
大型系统应用边界设计原则与实践
大型系统应用边界设计原则与实践
大型系统应用边界设计原则与实践
|
数据采集 运维 监控
构建数据网格分布式架构的四项原则
构建数据网格分布式架构的四项原则
131 0
构建数据网格分布式架构的四项原则
|
存储 弹性计算 5G
5G 网络重构关键技术 | 带你读《5G时代的承载网》之十五
5G 网络架构的重构是以一系列新技术的引入作为先决条件的,例如,基于 SDN 实现控制与转发的分离,基于 NFV 实现软硬件解耦。另外,还需要引入 网络切片、边缘计算、D2D 通信等技术方向,以形成针对所有场景的解决方案。 本节将对几个主要的 5G 使用技术做简单介绍。
5G 网络重构关键技术  | 带你读《5G时代的承载网》之十五
|
人工智能 运维 物联网
5G 承载技术|带你读《5G无线网络规划与设计》之八
在性能方面,5G承载网需具有更大带宽、超低时延和高精度同步,以满足 5G 三大应用场景的需求。在组网及功能方面,5G 承载网应实现多层级承载网络、灵活化连接调度、层次化网络切片、智能化协同管理、4G/5G 混合承载以及低成本高速组网等,促进承载资源的统一管理和灵活调度。
5G 承载技术|带你读《5G无线网络规划与设计》之八
|
安全 自动驾驶 物联网
5G 标准化现状|带你读《5G无线网络规划与设计》之二
推动 5G 加速部署的关键因素,同时也是 5G 商用部署的主要挑战和障碍。面向急速增长的移动数据流量需求,移动运营商面临着前所未有的巨大压力。随着业务多样性的发展,运营商的竞争对手在类型和数量上都在向全 IP 世界扩展。这些新的竞争对手有些可以以更低的成本提供服务,有些具有更灵活的开发环境,便于加快产品上市的速度。
5G 标准化现状|带你读《5G无线网络规划与设计》之二

相关实验场景

更多