侧边栏壁纸
博主头像
Hoo - Dev Tools Kit

开发工具箱

  • 累计撰写 5 篇文章
  • 累计创建 0 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

关于跨域问题

Hoo
Hoo
2024-10-04 / 0 评论 / 1 点赞 / 37 阅读 / 0 字

关于跨域问题

在解释和如何解决这个问题之前,我们先来看为什么会出现这个问题。

浏览器的同源策略

浏览器处于安全考虑,

同源请求 放行,对 异源请求 限制

这些限制规则统称为 同源策略

因此限制造成的开发问题,称之为 跨域(异源)问题

这里的关键就是 浏览器

  1. 什么是 同源

    源 = 协议 + 域名 + 端口

    源1源2是否同源?
    http://a.com:81/ahttps://a.com:81/a
    http://a.com:81/ahttp://www.a.com:81/a
    http://a.com:81/ahttp://a.com:82/a
    http://a.com:81/ahttp://a.com:81/a/b✔️
  2. 何为 同源请求

    何为同源请求-图1

  3. 浏览器如何 限制

    标签 发出的跨域请求 轻微限制

    例如:link引入的css、script引入的js、img、video、audio

    Ajax 发出的跨域请求 严厉限制

    对AJAX的限制-图1

    对AJAX的限制-图2

做个小测试,下面的说法正确吗?

  1. 我们的前端服务器请求后端服务器发生了跨域问题 ❌

    跨域问题只发生在浏览器和服务器之间,而服务器与服务器之间是没有跨域问题的

  2. 当发生跨域时,服务器无法收到请求 ❌

    可以收到,还会响应,只不过被浏览器拦截校验了

  3. 即便是发生跨域也不一定引发跨域问题 ✔️

  4. 跨域仅发生在AJAX过程中 ❌

    只要是异源请求,都属于跨域。只不过浏览器对这种标签的跨域限制不多,平时开发的时候感受不到。

要解决跨域的问题,关键点就在于了解浏览器是如何进行校验的,它校验的规则是什么

CORS 规则(Cross-Origin Resource Sharing)

CORS 是一套机制,用于浏览器校验跨域请求

它的基本理念是:

只要 服务器 明确表示 允许 ,则校验 通过

服务器 明确 拒绝没有表示 ,则校验 不通过

所以要用 CORS 来解决跨域问题的话,主要是靠 服务器

CROS规则-图1

使用 CORS 方案的前提,必须保证服务器是 【自己人】

CORS 将请求分为两类:

  1. 简单请求
    • 请求方法为:GET HEAD POST
    • 头部字段满足 CORS 安全规范,详见 W3C ( 粗略地 来说不改动头部的就算简单请求)
    • 请求头的 Content-Type 为:
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
  2. 预检请求

小练习,下面的跨域请求哪些是简单请求,哪些是预检请求:

// 1
fetch("https://douyin.com");

答:简单请求。请求方法:GET;请求头无更改。

CROS规则-图2

CROS规则-图3

// 2
fetch("https://douyin.com", {
  headers: {
    a: 1,
  },
});

答:预检请求。请求方法 GET ,请求头有更改。

CROS规则-图4

CROS规则-图5

// 3
fetch("http://douyin.com", {
  method: "POST",
  body: JSON.stringify({ a: 1, b: 2 }),
});

答:简单请求。请求方法 POST,请求头无更改。

CROS规则-图6

CROS规则-图7

// 4
fetch("https://douyin.com", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ a: 1, b: 2 }),
});

答:预检请求。请求方法 POST ,请求头有更改。

CROS规则-图8

CROS规则-图9

具体是怎么做的

1. 简单请求

浏览器在发送请求的时候,发现跨域了,此时它会自动带上一个请求头:Origin: 当前的页面源 ,让服务器去验证。

当服务器验证这个页面源是【自己人】,会告诉浏览器放行。”告诉“的方法有两种:

第一种给它一个响应头,后面跟上页面源:Access-Control-Allow-Origin: 页面源

第二种给它一个响应头,后面给一个*,意思是开放所有源

CROS规则-图10

2. 预检请求

浏览器会先发送一段预检请求到服务器,询问是否要让 Origin 通过。只有当服务器说是自己人的时候,浏览器才会放行。在此前提下,浏览器才会按照简单请求的方式发送请求给服务器。

  1. 发送预检请求

CROS规则-图11

  1. 发送真实请求

    简单请求

来做几个小练习,回答下面的问题:

  1. 领导让前端小王使用 CORS 在页面上自行解决跨域问题。前端小王可以完成要求吗?

    答:因为 CORS 是要动服务器的,除非小王自己写个前端服务器,否则解决不了。

  2. 前端小王在页面中想使用 AJAX 调用抖音的 API 接口,却发生了跨域问题。小王想通过 CORS 来解决这个问题。前端小王可以解决吗?

    答:小王可以打电话给抖音服务器负责人,让ta帮忙开通下权限。如果不行,小王解决不了。

  3. 跨域上传图片没有问题,但是提交普通表单却遇到了跨域问题。前端小王自己分析后,认为是服务器不支持预检请求导致的。前端小王分析的结果有可能发生吗?

    答:有可能。上传图片用的请求方法是:POST ,用的请求头是:Content-Type: multipart/form-data,所以很有可能上传图片用的是简单请求,而且很可能服务器是支持简单请求的。当我们在提交普通表单的时候,可能会使用 AJAX 以 json 的形式提交表单,那么这里就涉及到要改动请求头了,也就涉及到预检请求了,而题目说提交表单的时候遇到跨域问题,所以很有可能是服务器不支持预检请求导致的。

JSONP (JSON with Padding)实现跨域

在没有 CORS 的年代,又是怎么通过跨域请求的呢?

JSONP-图1

JSONP 是解决跨域问题的古老方案。

同源策略中,对标签的跨域请求限制较小

JSONP 利用了这一点。

那么具体又是怎么来操作的呢?

JSONP.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button>点击获取用户</button>
    <script>
      // 准备一个回调函数
      // 服务器响应后会运行该函数
      // 并传递响应数据给参数
      function callback(resp) {
        console.log(resp);
      }

      function request(url) {
        const script = document.createElement("script");
        script.src = url;
        script.onload = function () {
          // 当执行完毕后,删除掉这个元素
          script.remove();
        };
        document.body.appendChild(script);
      }

      document.querySelector("button").onclick = function () {
        request("https://xxxx.com/yyyy");
      };
    </script>
  </body>
</html>

当服务器的响应结果是 callback(data) 的时候,就会触发回调函数 callback ,此时 data 会放入到 resp 当中,届时我们就能拿到响应的数据了。

注意哦,这里不是 Ajax 请求,而是 JS 请求

so smart~

Proxy代理服务器

代理服务器图1

代理服务器图2

代理服务器图3

示例

前端小王想要请求获取王者荣耀英雄数据

但直接请求腾讯服务器会造成跨域

代理服务器图4

由于小马不接电话

前端小王决定使用代理服务器解决问题,下面是他的解决步骤

  1. 前端小王使用 node 搭建了一个服务器充当代理。他首先测试了浏览器和代理服务器的连通情况

    使用 node 搭建服务器

    index.js

    const express = require("express");
    const app = express(); //创建服务器
    
    // 接受对路径 /hero 的 GET 请求
    app.get("/hero", async (req, res) => {
      // 使用 CORS 解决对代理服务器的跨域
      res.header("access-control-allow-origin", "*");
      // 响应一段测试文本
      res.send("你好,我是你忠实的代理");
    });
    
    // 监听9527端口
    app.listen(9527, () => {
      console.log("服务器已启动");
    });
    

    npm run start(node index.js) 接受 9527 端口请求 hero 路径,返回的响应结果:

    代理服务器图6

    使用别的端口访问试试看:

    fetch("http://localhost:9527/hero").then(resp=>resp.text()).then(resp=>console.log(resp))

    代理服务器图7

    没有问题,说明浏览器和我们的代理服务器之间是连通的

    代理服务器图5

  2. 前端小王在代理服务器中加入了逻辑:当用户请求 /hero 的时候,转发请求到腾讯服务器获取数据

    index.js

    const express = require("express");
    const app = express(); //创建服务器
    
    // 接受对路径 /hero 的 GET 请求
    app.get("/hero", async (req, res) => {
      // 帮忙去请求王者荣耀的服务器
      const axios = require("axios");
    
      const resp = await axios.get("https://pvp.qq.com/web201605/js/herolist.json");
    
      // 使用 CORS 解决对代理服务器的跨域
      res.header("access-control-allow-origin", "*");
      // 将返回的数据响应给浏览器
      res.send(resp.data);
    });
    
    // 监听9527端口
    app.listen(9527, () => {
      console.log("服务器已启动");
    });
    

    Terminal: npm run start(node index.js)

    使用别的端口访问试试看:

    fetch("http://localhost:9527/hero").then(resp=>resp.json()).then(resp=>console.log(resp))

    代理服务器图8

    代理服务器图9

跨域方案经典场景

解决方案决策图

经典场景-图1

经典场景1

前端小王希望通过 AJAX 访问 【豆瓣电影】 的 API 接口,将它的电影数据展现到自己的网站上。

https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_start=0

小王遇到了跨域问题

该跨域问题应该用哪种方式解决最合适?

  • CORS
  • JSONP
  • 代理 ✔️
  • 在地上打滚

无法动服务器

经典场景2

公司的后端服务器是支持 CORS 跨域的,但最近前端小王在开发时发现一个奇怪的现象:

  1. 当本地使用 localhost:5000 打开页面后,请求服务器不会引发跨域问题
  2. 当本地使用 127.0.0.1:5000 打开页面后,请求服务器出现了跨域错误

前端小王要做的事情是:

  • 提着棍子找后端
  • 怒砸公司电脑
  • 吐口水
  • 对比前后服务器的响应头 ✔️

打开 Network 面板,查看响应头中是否有包含 CORS 里需要包含的内容

通过上面的步骤,前端小王确定了,当本地源是 127.0.0.1 时,服务器不产生 Access-Control-Allow-Origin 响应头

前端小王接下来做什么事可能解决该问题:

  • 继续使用 localhost:5000 打开页面 ✔️
  • 提着棍子找后端 ✔️
  • 端着茶找后端 ✔️
  • 用头撞墙

经典场景3

前端小王所在的项目组维护者一个老项目,老项目中使用的是 JSONP 处理的跨域。

目前需要新增一个功能,新功能中要用到 AJAX 跨域提交 POST 请求。

继续使用 JSONP 能办到吗?

  • 不能 ✔️ JSONP的一个很大的缺陷就是不能发送 POST 请求
  • 需要试一试才知道

应该如何才能完成这个新功能的跨域请求

  • CORS ✔️
  • JSONP
  • 代理 ✔️

经典场景4

前端小王入职后,项目负责人给了他一张公司服务器的部署图,

同时告知了小王,为了安全,nginx服务器不提供跨域解决方案

经典场景-图2

生产环境(用户使用):

http://a.com/index.html 访问静态资源

http://a.com/api/movie 访问 api

开发环境(开发人员使用):

http://localhost:8080/index.html 访问静态资源

http://test.a.com/api/movie 访问 api

以上信息意味着:

  • 生产环境会发生跨域问题
  • 生产环境不会发生跨域问题 ✔️
  • 开发环境会发生跨域问题 ✔️
  • 开发环境不会发生跨域问题

这同时意味着,小王需要解决:

  • 生产环境的跨域问题
  • 开发环境的跨域问题 ✔️

解决的方式是:

  • CORS

  • JSONP

  • 代理 ✔️ 在开发环境下,不要直接去请求 nginx 服务器了,直接去请求 dev-server ,让它把请求转发到 nginx 服务器,然后 nginx 服务器去拿数据,拿完数据再返回给 dev-server,然后它再把返回的数据返回给浏览器。

    开发环境(开发人员使用):

    http://localhost:8080/index.html 访问静态资源

    http://localhost:8080/api/movie =====转发=====> http://test.a.com/api/movie 访问 api

参考资料:

https://www.bilibili.com/video/BV13F411y7fy 浏览器的同源策略 【渡一教育】

https://www.bilibili.com/video/BV1rp4y1K7nU CORS跨域请求 【渡一教育】

https://www.bilibili.com/video/BV1SN411B7LX JSONP的原理是什么?它是如何实现跨域的?【渡一教育】

https://www.bilibili.com/video/BV1dh4y1K7Fm 跨域问题解决方案之代理 【渡一教育】

https://www.bilibili.com/video/BV1Km4y1u7Sd 前端常见跨域问题及解决方案 【渡一教育】

1

评论区