深入解析:前端跨域问题及其CORS、代理、JSONP、Nginx反向代理等解决方案

news2024/12/24 9:59:04

在这里插入图片描述

前端跨域是指在浏览器环境下,当一个网页(源)尝试访问与自身源不同的服务器资源(目标源)时,由于浏览器的同源策略限制而产生的访问限制现象。同源策略(Same-Origin Policy)是浏览器实施的一种安全机制,用于防止恶意网站通过脚本对其他网站的数据进行非法访问,以保护用户的隐私和安全。

按照同源策略的规定,只有当请求的源(即协议、域名、端口号三者完全相同)与目标源相匹配时,浏览器才会允许脚本(如JavaScript)发起的网络请求(如Ajax、Fetch、XMLHttpRequest)成功访问目标源的资源。否则,这些请求会被浏览器视为跨域请求,并可能被拦截或阻止。

具体来说,以下情况会被浏览器判定为跨域:

  • 协议不同:如一个HTTPS页面尝试访问HTTP资源。
  • 域名不同:如www.example.com的页面尝试访问api.example.org的API。
  • 端口号不同:如localhost:3000的页面尝试访问localhost:8080的服务。

跨域问题主要影响浏览器端的JavaScript在以下场景中的行为:

  1. AJAX请求:JavaScript通过XMLHttpRequest或Fetch API发起的异步数据请求。
  2. WebSockets连接:JavaScript创建的WebSocket连接,用于建立浏览器与服务器间的双向通信。
  3. 嵌入资源:如尝试在页面中通过<img>, <script>, <link>, <iframe>等标签加载跨域的图片、脚本、样式表或嵌入式页面。
  4. 跨窗口交互:如尝试通过window.postMessage()方法与不同源的窗口进行通信。

虽然同源策略对大部分跨域请求进行了限制,但对某些特定类型的资源(如上述的嵌入资源)和某些特定操作(如跨窗口通信)放宽了限制。此外,现代Web开发中也发展出了一系列技术与策略来解决或绕过跨域限制,包括但不限于:

  • CORS(Cross-Origin Resource Sharing,跨源资源共享):服务器通过返回特定的HTTP响应头,允许浏览器跨域访问其资源。
  • 代理:通过在本地或服务器端设置代理,将跨域请求转发至目标服务器,使得浏览器认为请求在同一源内。
  • JSONP(JSON with Padding):利用 <script> 标签不受同源策略限制的特性,通过动态插入脚本标签加载跨域数据。
  • iframe与postMessage:在同源的iframe中加载跨域资源,然后通过window.postMessage()进行跨窗口通信。
  • Nginx反向代理:在生产环境中,使用Nginx等Web服务器作为反向代理,转发客户端的跨域请求,并在响应时添加CORS头。

前端开发者在遇到跨域问题时,需要根据项目需求、开发环境和服务器配置选择适用的跨域解决方案,以确保浏览器端能够顺利访问所需跨域资源。

以下是6种常见的前端跨域解决方案的详解与举例:

1. CORS(Cross-Origin Resource Sharing,跨源资源共享)

原理:CORS是一种W3C标准,通过服务器返回特定的HTTP响应头,允许浏览器与服务器之间进行跨源通信。服务器明确声明哪些源可以访问其资源,以及允许的请求方法、头部和预检要求。

举例:服务器端(如Node.js Express应用)设置CORS响应头:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有源或指定源
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); // 允许的方法
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 允许的头部
  if (req.method === 'OPTIONS') {
    res.sendStatus(204); // 对预检请求(OPTIONS)返回空响应体
  } else {
    next(); // 其他请求继续处理
  }
});

app.get('/data', (req, res) => {
  res.json({ message: 'Hello, CORS!' });
});

app.listen(3000, () => {
  console.log('CORS-enabled server listening on port 3000');
});

前端(如使用Fetch API)发起跨域请求:

fetch('http://example.com:3000/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

2. 代理(Proxy)

原理:通过本地开发服务器(如Webpack Dev Server、Vue CLI Serve)的代理功能,将前端发出的跨域请求转发到目标服务器。浏览器认为请求始终在同一源内,避免了同源策略限制。

举例:在Vue CLI创建的项目中,vue.config.js配置代理:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://example.com:8080', // 目标服务器地址
        changeOrigin: true, // 是否更改请求头中的Host
        pathRewrite: { '^/api': '' }, // 路径重写
      },
    },
  },
};

前端发起请求:

axios.get('/api/data')
  .then(response => console.log(response.data))
  .catch(error => console.error('Error:', error));

3. JSONP(JSON with Padding)

原理:利用 <script> 标签不受同源策略限制的特性,动态插入带有跨域URL的<script>标签。服务端返回一个调用前端预定义回调函数的JS代码片段,将数据作为参数传递。

举例:前端定义回调函数:

function handleJsonpData(data) {
  console.log('Received JSONP data:', data);
}

const script = document.createElement('script');
script.src = 'http://example.com/jsonp?callback=handleJsonpData';
document.head.appendChild(script);

服务端响应JSONP请求(假设使用Node.js):

const http = require('http');

http.createServer((req, res) => {
  const callbackName = req.query.callback;
  const data = { message: 'Hello, JSONP!' };

  res.setHeader('Content-Type', 'application/javascript');
  res.end(`${callbackName}(${JSON.stringify(data)});`);
}).listen(8080, () => {
  console.log('JSONP server listening on port 8080');
});

4. Nginx反向代理

原理:在生产环境中,使用Nginx等Web服务器作为反向代理,将客户端的跨域请求转发到目标服务器,并在响应时添加CORS头。客户端感知不到实际服务器源,从而规避跨域限制。

举例:Nginx配置文件中的反向代理配置:

server {
  listen 80;

  location /api {
    proxy_pass http://backend.example.com:8080; // 目标服务器地址
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
  }
}

前端请求保持不变,访问代理服务器地址:

fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

5. iframe与postMessage

原理:将跨域请求封装在同源的iframe中,通过window.postMessage方法实现跨窗口通信,使主窗口能够获取到iframe内加载的跨域资源数据。

举例:主页面创建iframe并监听message事件:

<iframe id="cross-origin-frame" src="http://example.com/cross-origin.html"></iframe>

<script>
  window.addEventListener('message', function(event) {
    if (event.origin !== 'http://example.com') return; // 验证来源
    console.log('Received data from iframe:', event.data);
  });

  // 如果需要与iframe通信,可以向其发送消息
  document.getElementById('cross-origin-frame').contentWindow.postMessage({ action: 'getData' }, 'http://example.com');
</script>

iframe页面接收消息并响应:

<script>
  window.addEventListener('message', function(event) {
    if (event.origin !== 'http://localhost:3000') return; // 验证来源
    if (event.data.action === 'getData') {
      fetch('/api/data')
        .then(response => response.json())
        .then(data => {
          event.source.postMessage(data, event.origin); // 将数据回传给主页面
        })
        .catch(error => console.error('Error:', error));
    }
  });
</script>

6. WebSocket

原理:WebSocket协议支持浏览器与服务器之间建立全双工的持久连接,一旦连接建立,客户端与服务器端就可以自由地互相发送数据,不受同源策略限制。

举例:前端创建WebSocket连接:

const socket = new WebSocket('ws://example.com/ws');

socket.addEventListener('open', function(event) {
  console.log('WebSocket connection established.');
});

socket.addEventListener('message', function(event) {
  console.log('Received data from server:', event.data);
});

// 发送数据到服务器
socket.send(JSON.stringify({ message: 'Hello, WebSocket!' }));

服务器端(假设使用Node.js的ws库):

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('Received data from client:', message);

    const data = { response: 'Hello, client!' };
    ws.send(JSON.stringify(data)); // 回传数据到客户端
  });
});

综上所述,前端跨域问题有多种解决方案,其中CORS、代理(尤其是开发阶段的代理)、JSONP、Nginx反向代理是较为常用且现代的解决手段。根据项目需求和环境,选择合适的跨域策略。对于更复杂或特定场景,可以考虑使用iframe与postMessage、WebSocket等技术。
在这里插入图片描述

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

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

相关文章

芜湖市夜间景区、文娱主题活动、夜读空、精品文艺演出、数字促销补助等夜间经济奖励政策申报条件、材料

芜湖市示范街区、示范门店、夜间景区、文娱主题活动、体育赛事、夜读空、精品文艺演出、数字促销补助等夜间经济奖励政策申报条件、材料及补贴标准整理如下 芜湖市2023年促进夜间经济发展若干政策申报时间&#xff1a; 针对2023年度促进夜间经济发展若干政策&#xff08;商务局…

❤️新版Linux零基础快速入门到精通——第一部分❤️

❤️新版Linux零基础快速入门到精通——第一部分❤️ 非科班的我&#xff01;Ta&#xff01;还是来了~~~1. 来认识一下Linux吧!1.1 操作系统概述1.1.1 操作系统概述1.1.2 操作系统的发展史1.1.2.1 Unix1.1.2.2 Minix1.1.2.3 Linux 1.1.3 操作系统的发展 1.2 Linux初识1.2.1 Lin…

二叉检索树的实现——增删改查、读取命令文件、将结果写入新文件

看这篇文章前的知识储备 链接: 二叉树的性质和分类 链接: 二叉检索树的概念 、insert方法的图解、实现、时间代价分析 链接: 二叉检索树的search、remove方法的图解、实现、时间代价分析 1、中序遍历及中序遍历写进文件的区别 两者思路一致&#xff0c;将二叉树分为三部分&…

Linux信号(产生)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 信号是什么&#xff1f; 为什么要有信号&#xff1f; 信号是如何产生的&#xff1f; kill命令 键盘产生信号 系统调用 kill系统调用 raise函数 abort函数 自制kill命令 ​编辑 软件条件 举例一&#xff1…

C++ :设计模式实现

文章目录 原则单一职责原则开闭原则依赖倒置原则接口隔离原则里氏替换原则 设计模式单例模式观察者模式策略模式代理模式 原则 单一职责原则 定义&#xff1a; 即一个类只负责一项职责 问题&#xff1a; 类 T 负责两个不同的职责&#xff1a;职责 P1&#xff0c;职责 P2。当…

大数据第六天

这里写目录标题 问题解决问题查询插入(时间慢)练习sql数据清理 问题 FAILED: ParseException line 1:16 mismatched input ‘input’ expecting INPATH near ‘local’ in load statement MismatchedTokenException(24!155) 加载数据的时候出现了这个错误&#xff0c;我们解释…

【六十】【算法分析与设计】用一道题目解决dfs深度优先遍历,dfs中节点信息,dfs递归函数模板进入前维护出去前回溯,唯一解的剪枝飞升返回值true

路径之谜 题目描述 小明冒充X星球的骑士,进入了一个奇怪的城堡。 城堡里边什么都没有,只有方形石头铺成的地面。 假设城堡地面是nn个方格。如下图所示。 按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着音走,也不能跳跃。每走到一个新方格,就要向正北 方和正西…

短信视频提取批量工具,免COOKIE,博主视频下载抓取,爬虫

痛点&#xff1a;关于看了好多市面的软件&#xff0c;必须要先登录自己的Dy号才能 然后找到自己的COOKIE 放入软件才可以继续搜索&#xff0c;并且无法避免长时间使用 会导致无法正常显示页面的问题。 有没有一种方法 直接可以使用软件&#xff0c;不用设置的COOKIE的方法呢 …

对于地理空间数据,PostGIS扩展如何在PostgreSQL中存储和查询地理信息?

文章目录 一、PostGIS扩展简介二、PostGIS存储地理空间数据1. 创建空间数据表2. 插入空间数据 三、PostGIS查询地理空间数据1. 查询指定范围内的地理空间数据2. 计算地理空间数据之间的距离3. 对地理空间数据进行缓冲区分析 四、总结 地理空间数据是指描述地球表面物体位置、形…

开源社区与开发者的故事

开源社区与开发者的故事 什么是开源社区你参加开源社区的主要目的你是否在开源社区中贡献&#xff0c;或者开源自己的项目&#xff1f;你认为个人开发者是否应该从开源中获利&#xff1f;如果是&#xff0c;该如何获利&#xff1f; 今天要谈及的主题是开源社区&#xff0c;那么…

2024年新算法-牛顿-拉夫逊优化算法(NRBO)优化BP神经网络回归预测

亮点&#xff1a; 输出多个评价指标&#xff1a;R2&#xff0c;RMSE&#xff0c;MSE&#xff0c;MAPE和MAE 满足需求&#xff0c;分开运行和对比的都有对应的主函数&#xff1a;main_BP, main_NRBO, main_BPvsBP_NRBO&#xff0c;并且详细中文注释 方便快捷&#xff1a;替换…

打破企业差旅管理困局,让金融CEO眼前一亮的出行方案

在国内券商投行部工作是怎样一种体验&#xff1f; “长期出差&#xff0c;而且出长差&#xff0c;时常让人有漂泊的孤独感。”这是某问答平台上的高赞回答的第一条。 对金融人来说&#xff0c;说走就走的旅行可能根本没有什么吸引力&#xff0c;时刻准备着说走就走的出差才是生…

MVCC的执行原理

MVCC的执行原理 MVCC简介事务的隔离级别MVCC作用当前读和快照读MVCC实现原理Undo LogUndo Log 版本链Read View判断方法判断规则 小结 MVCC简介 MVCC&#xff08;Multi-Version Concurrency Control&#xff09;是一种并发控制机制&#xff0c;用于解决数据库并发访问中&#…

pyqt 动态更换表头和数据

目录 pyqt 动态更换表头和数据代码 效果图&#xff1a; pyqt 动态更换表头和数据代码 from PyQt5.QtGui import QColor, QBrush from PyQt5.QtWidgets import QApplication, QTableWidget, QVBoxLayout, QWidget, QPushButton, QTableWidgetItemclass Example(QWidget):def _…

如何诊断并解决PostgreSQL中的磁盘空间不足问题?

文章目录 诊断磁盘空间不足问题1. 检查服务器磁盘空间2. 检查PostgreSQL数据目录大小3. 检查PostgreSQL中的大表和大对象 解决磁盘空间不足问题1. 清理不必要的文件和日志2. 清理或压缩大表和大对象3. 扩展磁盘容量4. 优化数据库配置和查询 在使用PostgreSQL数据库时&#xff0…

华为云实验 -- 对云硬盘数据盘进行备份

文章目录 备份Linux系统备份1.购买Linux操作系统的ESC(云服务器)2.挂载数据盘--初始化--分区--格式化2.1.点击"远程登录"a.查看/dev/vdb数据盘b.新建主分区/dev/vdb1 2.2.查看新建分区大小,分区格式信息a.确定之前的分区操作是否正确b.确认完成后&#xff0c;将分区结…

【MATLAB源码-第32期】基于matlab的通信及雷达中常用伪随机码m序列的仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 M序列&#xff0c;也称为最大长度序列或者伪随机序列&#xff0c;是一种特殊的二进制序列。它的特点是在有限的长度内&#xff0c;尽管它是伪随机的&#xff0c;但它会在特定的周期内不重复地循环。 在数学上&#xff0c;M序…

利用fft算法重写公式并理解频率和像素变化率的关系(完美解决问题)

算法我就不贴了。算法就是算法导论的内容。 我直接写推导过程。 假设变化率为f(n1)-f(n) 首先计算二进制数&#xff0c;这里我假设为3位二进制。 例如:f(5)-f(4)&#xff0c; 5和4的二进制为101,100。所以逆序数为101&#xff0c;001 101对应的频率为5, 001对应的频率为1…

力扣HOT100 - 236. 二叉树的最近公共祖先

解题思路&#xff1a; dfs 节点p,q异侧时&#xff0c;节点root为它们的公共祖先。 class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root null || p root || q root) return root;TreeNode left lowestCommonAncest…

max各种相机导出到ue4匹配镜头的工具集

总览 rollout export_UE4Cam_v2 "导出UE4Cam_v2:半自动" width:200 height:120(HyperLink explain "在打开的max文件中使用" pos:[25,12] width:200 height:15 color:(color 255 155 0) GroupBox grp1 "要导出的相机名" pos:[5,28] width:179 …