跨域的多种方案详解

news2024/11/25 1:22:58

        浏览器的同源策略是为了保护用户的安全,限制了跨域请求。同源策略要求请求的域名、协议和端口必须完全一致,只要有一个不同就会被认为是跨域请求。

本文列举了一些处理跨域请求的解决方案:

  •  JSONP
  • CORS跨域资源共享
  • http  proxy
  • nginx反向代理
  • webSocket 协议跨域
  • postMessage
  • document.domain + iframe
  • window.name + iframe
  • location.hash + iframe

1. JSONP

先说缺点,JSONP只能处理get请求。

script、iframe、img、link 等src属性,不存在跨域请求的限制。利用这个特性可以实现跨域请求

首先在服务器端用node+express模拟一个接口

let express = require("express");
let app = express();

app.listen(8001, (_) => {
  console.log("服务器已启动!");
});

app.get("/list", (req, res) => {
    // let { callback = Function.prototype } = req.query;
  let callback = req.query.callback;

  let data = {
    code: 200,
    messsage: "程序猿小野",
  };

  res.send(`${callback}(${JSON.stringify(data)})`);
});

1)使用jquery的ajax实现(axios的jsonp方法也可以)

$.ajax({
    url: "http://127.0.0.1:8001/list",
    method: "get",
    dataType: "jsonp", // => 执行的JSONP的请求,不加这一行会报跨域错误,has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    success: (res) => {
    console.log(res);
    },
});

web本地启动的服务器地址是:http://127.0.0.1:5501,服务器的地址是:http://127.0.0.1:8001/

不加dataType:"jsonp"时会报下面的跨域错误

2)利用 script标签的src属性

function handleResponse(data) {
    console.log("接收到的数据:", data);
}

let scriptEle = document.createElement("script");
scriptEle.src = `http://127.0.0.1:8001/list?callback=handleResponse`;
document.head.appendChild(scriptEle);

成功接收到数据: 


2. CORS跨域资源共享(必须掌握)

<script>
    axios
    .get("http://127.0.0.1:8001/list")
    .then((res) => {
        console.log(res.data);
    })
    .catch((err) => {
        console.error(err);
    });
</script>

let express = require("express");
let app = express();

app.listen(8001, (_) => {
  console.log("服务器已启动!");
});

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "http://127.0.0.1:5501");
//   res.header("Access-Control-Allow-Origin", "*");
//   res.header("Access-Control-Allow-Credentials", "true"); // 后端允许发送Cookie
  next();
});

app.get("/list", (req, res) => {

  let data = {
    code: 200,
    messsage: "程序猿小野",
  };

  res.send(`${JSON.stringify(data)}`);
});


3. http  proxy (必须掌握)

这种情况是目前开发时,前端调试接口解决跨域问题,最常见的处理方法。一般通过webpack webpack-dev-server实现。用vue-cli创建的项目可以在vue.config.js中配置。

index.js

import axios from "axios";

// axios.get("http://127.0.0.1:8001/user/list").then((res) => {
axios.get("/user/list").then((res) => {
  console.log(res);
});

 没有配置proxy代理时,请求报错:

vue.config.js配置示例:

devServer: {
    open: true,
    port: 10003,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
    proxy: {
      "/": {
        target: "http://127.0.0.1:8001/",
        secure: false,
        changeOrigin: true,
      },
    },
  },

 webpack.config.js 配置示例:

let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  output: {
    filename: "bundle.min.js",
    path: path.resolve(__dirname, "build")
  },
  devServer: {
    port: 3000,
    progress: true,
    contentBase: "./build",
    proxy: {
      "/": {
        target: "http://127.0.0.1:8001",
        changeOrigin: true
      },
    },
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      filename: "index.html",
    })
  ],
};

配置后请求成功:


4. nginx反向代理(必须掌握)

nginx 相关的内容比较多,有时间可以另开一个帖子去说。这里举一个我在实际开发过程中遇到的场景。我们的页面调其他前端模块的服务跨域了,在开发中没有,为什么上线后会出现呢。主要是因为开发时前端的服务都在同一个服务器上,不会产生跨域情况。上线部署在不同的服务器,这时候就出现了如下错误:

修改ngnix的相关配置,实际ip地址和对应的服务名已处理。修改后跨域问题解决:

upstream testxxx {
    server 127.0.0.1:3000 weight=1;
    server 127.0.0.2:4000 weight=1;
}

location /fuwuming{
    proxy_set_header Host $host:$server_port;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://testxxx/fuwuming;
}

查看结果: 


5. webSocket 协议跨域(必须掌握)

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生WebSocket API使用起来不太方便,下面案例借助了socket.io.js的库,,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。 

前端代码:

<!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>
    <div>
      user input:<input type="text" /><button id="button">发送</button>
    </div>
    <script src="./socket.io-4.7.2.js"></script>
    <script>
      var socket = io("http://127.0.0.1:3000");

      // 与服务器连接成功
      socket.on("connect", () => {
        console.log("socket链接成功!" + socket.id);
      });

      // 接收服务器发送的消息
      socket.on("sendToClient", (message) => {
        console.log(message);
      });

      let sendToServerFunc = function () {
        let inputVal = document.getElementsByTagName("input")[0].value;
        // 向服务器发送消息
        socket.emit("sendToServer", {
          message: `我是客户端: ${inputVal}`,
        });
      };

      document.getElementsByTagName("input")[0].onblur = sendToServerFunc;

      document.getElementById("button").onclick = sendToServerFunc;
    </script>
  </body>
</html>

node服务器代码:

// 导入express模块
const express = require('express')
// 引入http创建服务器实例的方法
const {createServer} = require('http')
const {Server} = require('socket.io')
// 创建express的服务器实例
const app = express()
// 创建http服务器实例
const httpServer = createServer(app)
// 创建socket.io的实例
const io = new Server(httpServer,{
  // 处理cors,解决跨域问题
  cors:{
    // origin: "http://127.0.0.1:5500",//需要跨域资源共享的地址
    origin: "http://127.0.0.1:5501",//需要跨域资源共享的地址
    allowedHeaders: ["my-custom-header"],
    credentials: true
  }
})
// 监听客户端连接,回调函数会传递本次连接的socket
io.on('connection',(socket) => {
  console.log(socket.id)
  // 监听到客户端发送的消息
  socket.on('sendToServer',(message) => {
    console.log(message)
    // 向客户端发送消息
    socket.emit('sendToClient',{
      message:'你好我是服务端,让我们来聊天呀'
    })
  })
} )
 
// 调用listen方法,指定端口号并启动web服务器
httpServer.listen(3000,() =>{
  console.log('server is running at http://127.0.0.1:3000')
})

效果如下:

 


6. postMessage

MDN关于postMessage的介绍:

 

 测试案列:

先用node启动两个服务器,不同端口,模拟跨域场景。

 node serverA.js     node serverB.js

let express = require("express");
let app = express();

app.listen(1001, (_) => {
  console.log("服务器A已启动!");
});

app.use(express.static("./"));
let express = require("express");
let app = express();

app.listen(1002, (_) => {
  console.log("服务器B已启动!");
});

app.use(express.static("./"));

A.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>
    <h1>A 页面</h1>

    <iframe id="iframe" src="http://127.0.0.1:1002/B.html" frameborder="0" style="display: none;"></iframe>

    <script>
        iframe.onload = function () {
            iframe.contentWindow.postMessage('这是A页面传过来的数据AAA', '*');
        }

        // 监听B页面传递的消息
        window.onmessage = function(res){
            console.log(res);
        }
    </script>
</body>
</html>

B.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>
    <h1>B 页面</h1>

    <script>
      // 监听A页面传递的消息
      window.onmessage = function (res) {
        console.log(res);
        res.source.postMessage("这是B页面传过来的数据BBB", res.origin);
      };
    </script>
  </body>
</html>

下面几种方案只做了解,实际上几乎用不到。 

7. document.domain + iframe (只做了解)

此方案只能实现:同一个主域,不同子域之间的操作

比如:http://www.domain.com/a.html  和  http://child.domain.com/b.html  属于同一个主域

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>
<script>
    document.domain = 'domain.com';
    // 获取父窗口中变量
    console.log(window.parent.user);
</script>

8. window.name + iframe(只做了解)

三个页面:A.html地址为:http://127.0.0.1:1001/A.html

B.html地址为:http://127.0.0.1:002/B.html

Proxy.html地址为:http://127.0.0.1:1002/proxy.html

可以看出B页面和Proxy页面同源,A想访问B的数据就跨域了

B.html的代码,可以看到B.html的window对象上有一个name属性

<!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>
    <script>
        // 服务器端需要返回给A的信息都在window.name中存储
      window.name = "这是B页面的数据";
    </script>
  </body>
</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>
    <iframe
      id="iframe"
      src="http://127.0.0.1:1002/B.html"
      frameborder="0"
      style="display: none"
    ></iframe>

    <script>
      iframe.onload = function () {
        // 直接读不允许;
        console.log(iframe.contentWindow.name);
      };
    </script>
  </body>
</html>

直接读不允许,控制报跨域错误 

 将iframe的src属性修改成和B.html同域的Proxy.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>
    <iframe
      id="iframe"
      src="http://127.0.0.1:1002/B.html"
      frameborder="0"
      style="display: none"
    ></iframe>

    <script>
      let count = 0;
      iframe.onload = function () {
        // 直接读不允许;
        // console.log(iframe.contentWindow.name);
        // 需要我们先把地址重新指向到同源中,才可以
        // iframe.src = 'http://127.0.0.1:1001/proxy.html';
        // 只要proxy.html中的window没有name属性,那么这里就会取到B.html中window.name
        // console.log(iframe.contentWindow.name);

        if (count === 0) {
          iframe.src = "http://127.0.0.1:1001/proxy.html";
          count++;
          return;
        }
        console.log(iframe.contentWindow.name);
      };
    </script>
  </body>
</html>

在A.html中成功读取到B.html的数据 

9. location.hash + iframe(只做了解)

A和C同源,A和B非同源

A.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>
    <iframe
      id="iframe"
      src="http://127.0.0.1:1002/B.html"
      frameborder="0"
      style="display: none"
    ></iframe>

    <script>
      let iframe = document.getElementById("iframe");
      iframe.onload = function () {
        iframe.src = "http://127.0.0.1:1002/B.html#msg=hello";
      };

      // 开放给同域C.html的回调方法
      function func(res) {
        console.log(res);
      }
    </script>
  </body>
</html>

B.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>
    <iframe
      id="iframe"
      src="http://127.0.0.1:1001/C.html"
      frameborder="0"
      style="display: none"
    ></iframe>

    <script>
      let iframe = document.getElementById("iframe");
      // 监听A传来的HASH值改变,再传给C.html
      window.onhashchange = function () {
        iframe.src = "http://127.0.0.1:1001/C.html" + location.hash;
      };
    </script>
  </body>
</html>

 C.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>
    <script>
      // 监听B传来的Hash值
      window.onhashchange = function () {
        // 再通过操作同域A的js回调,将结果传回
        window.parent.parent.func(location.hash);
      };
    </script>
  </body>
</html>

如果还有其他方案,欢迎留言补充。

总结一下:2、3、4、5 我个人觉得是必须要掌握的,因为我在实际的项目开发中都使用过。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1292507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第七次作业

1&#xff0c; 给定一个包含n1个整数的数组nums&#xff0c;其数字在1到n之间&#xff08;包含1和n)&#xff0c;可知至少存在一个重复的整数&#xff0c;假设只有一个重复的整数&#xff0c;请找出这个重复的数 arr input("") num [int(n) for n in arr.split()]…

嵌入式板级系统设计【课设】

笔记【嵌入式板级系统设计】 前言版权笔记【嵌入式板级系统设计】资料学习面包板焊接注意焊接教程 焊接电路板基础代码GPIO 外部中断 定时中断 三合一串口 综合实验 风扇控制系统下板三合一窗口综合实验 最后 前言 2023-11-20 08:49:57 以下内容源自《【创作模板五】》 仅供学…

通过K8S安装人大金仓数据库

1. 离线下载镜像&#xff0c;请点击 2. 官网下载镜像 https://www.kingbase.com.cn/xzzx/index.htm&#xff0c;根据自己的需求下载对应版本。 3. K8S需要的yaml清单 cat > kingbase.yaml << EOF apiVersion: apps/v1 kind: Deployment metadata:name: kingbase-…

将图像增广应用于Mnist数据集

将图像增广应用于Mnist数据集 不用到cifar-10的原因是要下载好久。。我就直接用在Mnist上了&#xff0c;先学会用 首先我们得了解一下图像增广的基本内容&#xff0c;这是我的一张猫图片&#xff0c;以下为先导入需要的包和展示图片 import time import torch from torch im…

javaTCP协议实现一对一聊天

我们首先要完成服务端&#xff0c;不然出错&#xff0c;运行也要先运行服务端&#xff0c;如果不先连接服务端&#xff0c;就不监听&#xff0c;那客户端不知道连接谁 服务端 import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.Actio…

超越GPT-4!谷歌发布最强多模态大模型—Gemini

12月7日凌晨&#xff0c;谷歌在官网发布了全新最强多模态大模型——Gemini。 据悉&#xff0c;Gemini有Ultra、Pro、Nano三个版本&#xff0c;可自动生成文本、代码、总结内容等&#xff0c;并能理解图片、音频和视频内容。在MMLU、DROP 、HellaSwag、GSM8K等主流评测中&#…

JVM虚拟机(已整理,已废弃)

# JVM组成 ## 简述程序计数器 线程私有&#xff0c;内部保存class字节码的行号。用于记录正在执行的字节码指令的地址。 线程私有-每个线程都有自己的程序计数器PC&#xff0c;用于记录当前线程执行哪个行号 ## 简述堆 ## 简述虚拟机栈 ## 简述堆栈区别 ## 方法内局部变量是…

【前端架构】清洁前端架构

探索前端架构&#xff1a;概述与干净的前端架构相关的一些原则&#xff08;SOLID、KISS、DRY、DDD等&#xff09;。 在我之前的一篇帖子中&#xff0c;我谈到了Signals和仍然缺少的内容[1]。现在&#xff0c;我想谈谈一个更通用的主题&#xff0c;即Clean Frontend Architectu…

python+paddleocr 进行图像识别、找到文字在屏幕中的位置

目录 前言 1、安装paddleocr 2、安装PIL 3、安装numpy 4、 安装pyautogui 5、进行文本识别 6、识别结果 7、获取文字在图片/屏幕中的位置 8、pyautoguipaddleocr鼠标操作 9、完整代码 前言 最近在做自动化测试&#xff0c;因为是处理过的界面&#xff0c;所以使用pyw…

Vue3项目调用腾讯地图服务(地址解析 地址转坐标)及使用axios的跨域问题

一,需求 根据传入的文本地址 将其转换为坐标 显示地图点位在腾讯地图上 二,使用axios发送请求 import axios from axios; //引入axiosaxios({url:https://apis.map.qq.com/ws/geocoder/v1,method:get//参数 地址和key值}).then((data)>{console.log(data)});但是使用完报跨…

猫咪瘦弱的原因是什么?适合给消瘦猫咪长肉吃的猫罐头分享

很多小猫咪吃得很多&#xff0c;但是还是很瘦&#xff0c;这让很多猫主人感到困惑&#xff0c;猫咪瘦弱的原因是什么呢&#xff1f;铲屎那么多年&#xff0c;还是有点子养猫知识在身上的。那么&#xff0c;小猫咪瘦弱的原因是什么呢&#xff1f;让我们看看是不是这些原因导致的…

为什么有些程序员宁愿在国内 35 岁被辞退,也不愿意去国外工作?

我发现IT圈和电竞圈有一个共性&#xff1a;菜是原罪。 为什么有些程序员35岁就会被辞退&#xff1f;因为菜。 为什么有些程序员不愿意去国外工作&#xff1f;因为菜。 当然&#xff0c;我这里指的菜不是烂泥扶不上墙的那种菜&#xff0c;而是不够拔尖。那么这个问题也就分为了三…

【项目日记(一)】高并发内存池项目介绍

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 项目日记 1. 前言2. 什么是高并发内存池…

基于ssm vue个人需求和地域特色的外卖推荐系统源码和论文

首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数据库设计。本项…

9种伪原创工具推荐,快速提升创作效率

如何让自己的文章在海量信息中脱颖而出&#xff0c;成为一个备受关注的焦点&#xff0c;成为许多创作者迫切思考的问题。在这篇文章中&#xff0c;我将向大家介绍9种伪原创工具&#xff0c;这些工具可以让你的文章轻松升级&#xff0c;更具创意和吸引力。 1.Spinbot&#xff08…

simulink中 Data store memory、write和read模块及案例介绍

目录 1.Data store memory模块 2.data store write模块 3.data store read模块 4.仿真分析 4.1简单使用三个模块 4.2 模块间的调用顺序剖析 1.Data store memory模块 向右拖拉得到Data store read模块&#xff0c;向左拉得到Data write模块 理解&#xff1a;可视为定义变量…

C++ 函数详解

目录 函数概述 函数的分类 函数的参数 函数的调用 函数的嵌套调用 函数的链式访问 函数声明和定义 函数递归 函数概述 函数——具有某种功能的代码块。 一个程序中我们经常会用到某种功能&#xff0c;如两数相加&#xff0c;如果每次都在需要用到时实现&#xff0c;那…

矩阵学习相关——(待完善)

线性代数基础知识之–矩阵&#xff08;Matrix&#xff09; 矩阵概念————&#xff08;基础知识&#xff09; 矩阵理论基础知识 矩阵理论基础知识 矩阵入门 写给有编程基础的人 初学讲义之高中数学二十七&#xff1a;矩阵和行列式 直观理解&#xff01;你一定要读…

C++多态(详解)

一、多态的概念 1.1、多态的概念 多态&#xff1a;多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 举个例子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票&#xff1b;学生买票时&am…

JavaScript实现手写签名,可触屏手写,支持移动端与PC端双端保存

目录 1.HTML模板 2.获取DOM元素和定义变量 3.创建两个canvas元素&#xff0c;并设置它们的宽度和高度 4.绑定触摸事件&#xff1a;touchstart, touchmove, touchend和click 5.实现触摸事件回调函数&#xff1a;startDrawing, draw和stopDrawing 6.实现绘制线段的函数&…