前端开发攻略---彻底弄懂跨域解决方案
目录
1、浏览器的同源策略
1.1 源
1.2 同源与非同源
1.3 同源请求与非同源请求
2、跨域受到的限制
3、注意点
4、CORS解决Ajax跨域问题
4.1 CORS概述
4.2 CORS解决简单请求跨域
4.3 简单请求与复杂请求
4.4 CORS解决复杂请求跨域
4.5 借助CORS库快速完成配置
5、JSONP解决跨域
6、配置代理解决跨域
6.1 自己配置代理服务器
6.2 使用Nginx搭建代理服务器
6.3 借助脚手架搭建代理服务器
1、浏览器的同源策略
浏览器为了确保资源安全,而遵循的一种策略。
1.1 源
源 = 协议 + 域名 + 端口号
1.2 同源与非同源

1.3 同源请求与非同源请求


所处源与目标源不一致,就是非同源,又称异源或跨域
2、跨域受到的限制
例如:源A和源B,它们是非同源,则浏览器会有如下限制
- DOM访问限制:源A的脚本不能读取和操作源B的DOM
- Cookie访问限制:源A不能访问源B的cookie
- Ajax响应数据限制:源A可以给源B发请求,但是无法获取源B的响应的数据
备注:在上述限制中,浏览器对Ajax获取数据的限制是影响最大的一个,且实际开发中经常遇到
3、注意点
- 跨域限制仅存在浏览器端,服务端不存在跨域限制
- 即使跨域了,Ajax请求也可以正常发出,但响应数据不会交给开发者
- <link>、<script>、<img>....这些标签发出的请求也可能跨域,只不过浏览器对标签跨域不做严格限制,对开发几乎无影响
4、CORS解决Ajax跨域问题
4.1 CORS概述
CORS全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照CORS规范,添加特定响应头来控制浏览器校验,规则如下:
- 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过
- 服务器明确表示允许跨域请求,则浏览器校验通过
备注:使用CORS解决跨域是最正统的方式,且要求服务器是“自己人”
4.2 CORS解决简单请求跨域
整体思路:服务器在给出响应时,通过添加Access-Control-Allow-Origin响应头,来明确表达允许某个源发起跨域请求,随后浏览器在校验时,直接通过
服务端核心代码,以(express框架为例)
const express = require('express')const app = express()const data = [{ id: 123, name: '张三' },{ id: 456, name: '李四' },{ id: 789, name: '赵云' }, ] app.get('/student', (req, res) => {res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')res.send(data) })app.listen(1234, () => {console.log('服务运行中...') })
4.3 简单请求与复杂请求
CORS会把请求分为两类、分别是:简单请求、复杂请求
简单请求 复杂请求 请求方法为:GET、HEAD、POST
- 不是简单请求,就是复杂请求
- 复杂请求会自动发送预检请求
请求字段要符合《CORS安全规范》
简记:只要不手动修改请求头,一般都能符合改规范
请求头的Content-Type的值只能是以下三种
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
关于预检请求:
- 发送时机:预检请求再实际跨域请求之前发出,是由浏览器自动发起的
- 主要作用:用于向服务器确认是否允许接下来的跨域请求
- 基本流程:先发起OPTIONS请求,如果通过预检,继续发起实际的跨域请求
- 请求头内容:一个OPTIONS预检请求,通常会包含如下请求头
请求头 含义 Origin 发起请求的源 Access-Control-Request-Method 实际请求的HTTP方法 Access-Control-Request-Headers 实际请求中使用的自定义头(如果有的话)
4.4 CORS解决复杂请求跨域
1、第一步:服务器先通过浏览器的预检请求,服务器需要返回如下响应头
响应头 含义 Access-Control-Allow-Origin 允许的源 Access-Control-Allow-Methods 允许的方法 Access-Control-Allow-Headers 允许的自定义头 Access-Control-Max-Age
预检请求的结果缓存时间(可选) 服务端核心代码:
const express = require('express')const app = express()const data = [{ id: 123, name: '张三' },{ id: 456, name: '李四' },{ id: 789, name: '赵云' }, ]app.options('/student', (req, res) => {res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')res.setHeader('Access-Control-Allow-Method', 'GET')res.setHeader('Access-Control-Allow-Headers', 'a,b,c')res.setHeader('Access-Control-Allow-Age', 9000)res.send() })app.get('/student', (req, res) => {res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')res.send(data) })app.listen(1234, () => {console.log('服务运行中...') })
4.5 借助CORS库快速完成配置
上述的配置中需要自己配置响应头,或者需要自己手动封装中间件,借助cors库,可以更方便完成配置
1、安装cors
npm i cors2、简单配置
app.use(cors())3、完整配置
const corsOption = {origin: 'http://127.0.0.1:5500', // 允许的源methods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'], // 允许的方法allowedHeaders: [], // 允许的自定义头exposedHeaders: [], // 要暴露的响应头optionsSuccessStatus: 200, // 预检请求成功的状态码 } app.use(cors(corsOption))
5、JSONP解决跨域
1、JSONP概述:JSONP是利用了<script>标签可以跨域加载脚本,且不受严格限制的特性,可以说是程序员智慧的结晶,早起一些浏览器不支持CORS时,可以靠JSONP解决跨域
2、基本流程
- 第一步:客户端创建一个<script>标签,并将其src属性设置为包含跨域请求的URL,同时准备一个回调函数,这个回调函数用于处理返回的数据
- 第二步:服务端接收到请求后,将数据封装在回调函数中并返回
- 第三步:客户端的回调函数被调用。数据以参数的形式传入回调函数。
3、图示
4、服务端核心代码:
const express = require('express') const app = express()const data = [{ id: 123, name: '张三' },{ id: 456, name: '李四' },{ id: 789, name: '赵云' }, ]app.get('/getData', (req, res) => {res.send(`fn(${JSON.stringify(data)})`) })app.listen(1234, () => {console.log('服务运行中...') })5、客户端核心代码:
<!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 onclick="getData()">获取数据</button></body><script>function fn(data) {console.log(data)}function getData() {const script = document.createElement('script')script.onload = () => {script.remove()}script.src = 'http://127.0.0.1:1234/getData'document.body.appendChild(script)}</script> </html>
6、配置代理解决跨域
6.1 自己配置代理服务器
借助 http-proxy-middleware 配置代理
const { createProxyMiddleware } = require('http-proxy-middleware')// 拦截所有带有'/api'的请求,转发给target app.use('/api',createProxyMiddleware({target: 'https://www.toutiao.com',changOrigin: true, // 允许跨域pathRewrite: {'^/api': '',},}) )6.2 使用Nginx搭建代理服务器
1. 安装 Nginx
首先,你需要在你的服务器上安装 Nginx。根据你的操作系统,安装步骤可能有所不同。
在 Ubuntu/Debian 系统上:
sudo apt update sudo apt install nginx在 CentOS/RHEL 系统上:
sudo yum install epel-release sudo yum install nginx在 Fedora 系统上:
sudo dnf install nginx安装完成后,启动 Nginx 并设置开机自启:
sudo systemctl start nginx sudo systemctl enable nginx2、配置 Nginx 作为代理服务器
接下来,配置 Nginx 作为代理服务器。你可以编辑 Nginx 配置文件,通常在
/etc/nginx/nginx.conf或/etc/nginx/sites-available/default,具体路径根据操作系统和 Nginx 版本可能有所不同。基本的代理配置示例如下:
- 打开配置文件:
sudo nano /etc/nginx/nginx.conf- 添加一个
server块来配置代理设置。例如,假设你想要将所有请求代理到http://backend-server:http {...server {listen 80;server_name your-domain.com;location / {proxy_pass http://backend-server; # 后端服务器的地址proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}... }这里的
proxy_pass指令将请求转发到http://backend-server。你可以根据需要更改为你实际的后端服务器地址。
proxy_set_header指令用于设置转发请求时的 HTTP 头信息。- 保存文件并退出编辑器。
- 测试配置是否正确:
sudo nginx -t- 重新加载 Nginx 配置:
sudo systemctl reload nginx3、验证代理设置
现在,你可以通过访问你的代理服务器地址来验证是否能够成功地将请求代理到后端服务器。例如,访问
http://your-domain.com,你应该能够看到来自http://backend-server的内容。4、其他配置(可选)
根据需求,你可能还需要进行其他配置,例如:
- 负载均衡: 如果你有多个后端服务器,可以使用
upstream块来进行负载均衡。- SSL/TLS 加密: 如果你需要 HTTPS,可以配置 SSL 证书来加密代理流量。
- 缓存: 配置 Nginx 缓存以提高性能。
6.3 借助脚手架搭建代理服务器
修改
vue.config.js文件:// vue.config.js module.exports = {devServer: {proxy: 'http://localhost:5000', // 代理到你的后端服务器// 或者使用对象形式配置多个代理/*proxy: {'/api': {target: 'http://localhost:5000',changeOrigin: true,pathRewrite: { '^/api': '' },},},*/}, };
相关文章:
前端开发攻略---彻底弄懂跨域解决方案
目录 1、浏览器的同源策略 1.1 源 1.2 同源与非同源 1.3 同源请求与非同源请求 2、跨域受到的限制 3、注意点 4、CORS解决Ajax跨域问题 4.1 CORS概述 4.2 CORS解决简单请求跨域 4.3 简单请求与复杂请求 4.4 CORS解决复杂请求跨域 4.5 借助CORS库快速完成配置 5、JS…...
【HeadFirst 设计模式】装饰者模式的C++实现
一、案例背景 Starbuzz是以扩张速度最快而闻名的咖啡连锁店。如果你在街角看到它的店,在对面街上肯定还会看到另一家。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的…… 购买咖啡时&am…...
大白话解释TCP的三次握手和四次挥手
你好,我是沐爸,欢迎点赞、收藏和关注。个人知乎 TCP的三次握手是浏览器与服务器建立连接的过程,而四次挥手,是两者断开连接的过程。今天把客户端和服务端当做两个人,通过打电话的方式解释连接建立和断开的过程。 TCP…...
asyncua模块实现OPC UA通讯
asyncua是OPCUA的python实现,使用起来非常方便,其github地址是https://github.com/FreeOpcUa/opcua-asyncio UaExpert是OPC UA Client的GUI工具,当编写好server代码后并运行,我们可以使用UaExpert去和server进行通信。UaExpert使…...
RabbitMQ的核心概念
RabbitMQ是一个消息中间件,也是一个生产者消费者模型,负责接收,存储和转发消息。 核心概念 Producer 生产者,是RabbitMQ Server的客户端,向RabbitMQ发送消息。 Consumer 消费者,是RabbitMQ Server的客…...
【vSphere 7/8】深入浅出 vSphere 证书 Ⅰ—— 初识和了解 vSphere证书
目录 摘要1. vSphere 安全证书1.1 vSphere 安全证书的类型和有效期 2. 在 vSphere Client 中初识 vSphere 证书2.1 vCenter 8.0.3 的 vSphere Client 界面2.2 vCenter Server 7.0 Update2 到 vCenter Server 8.0 Update 2 的 vSphere Client 界面2.3 vCenter Server 7.0 到 vCe…...
【云备份】服务端模块-热点管理
文章目录 0.回顾extern1.介绍2.实现思想3.代码测试代码 热点管理总结 0.回顾extern extern cloudBackup::DataManager *_dataManager extern 关键字用于声明一个全局变量或对象,而不定义它。这意味着 _dataManager 是一个指向 cloudBackup::DataManager 类型的指针…...
call apply bind特性及手动实现
call // 原生的call var foo { value: 1 };function bar(...args) {console.log("this", this.value, args); }bar.call(foo)// call 改变了bar的this指向 // bar函数执行了 // 等价于 // var foo { // name: "tengzhu", // sex: "man", …...
pygame开发课程系列(5): 游戏逻辑
第五章 游戏逻辑 在本章中,我们将探讨游戏开发中的核心逻辑,包括碰撞检测、分数系统和游戏状态管理。这些元素不仅是游戏功能的关键,还能显著提升游戏的趣味性和挑战性。 5.1 碰撞检测 碰撞检测是游戏开发中的一个重要方面,它用…...
嵌入式系统实时任务调度算法优化与实现
嵌入式系统实时任务调度算法优化与实现 目录 嵌入式系统实时任务调度算法优化与实现 引言 1.1 嵌入式系统的重要性 1.2 实时任务调度的重要性 实时任务的定义与分类 2.1 实时任务的定义 2.2 实时任务的分类 2.3 实时任务的其他分类方法 硬实时与软实时系统 3.1 硬实…...
Java:枚举转换
在Java中,你可以使用Enum.valueOf()方法将字符串转换为枚举常量。但是,如果你想要将枚举转换为其他类型,你需要自定义转换方法。以下是一个简单的例子,演示如何将枚举转换为整数: public enum Color {RED(1), GREEN(2…...
Vue、react父子组件生命周期
Vue 的父子组件生命周期 以下分为三部分,加载渲染阶段——更新阶段——销毁阶段,我们来一一介绍: 1、加载渲染阶段 在加载渲染阶段,一定得等子组件挂载完毕后,父组件才能挂载完毕,所以父组件的 mounted 在…...
HTML 基础要素解析
目录 HTML 初步认识 纯文本文件介绍 纯文本文件与其它文件的区别 Html介绍 HTML 骨架 文档类型(!DOCTYPE)声明 介绍 常用的 DOCTYPE 声明 meta标签 字符集 关键字和页面描述 HTML 初步认识 纯文本文件介绍 纯文本文件指的是仅包含文本内容&am…...
开源的向量数据库Milvus
Milvus是一款开源的向量数据库,专为处理向量搜索任务而设计,尤其擅长处理大规模向量数据的相似度检索。 官网地址:https://milvus.io/ 以下是关于Milvus的详细介绍: 一、基本概念 向量数据库:Milvus是一款云原生向量…...
设计模式-工厂方法
“对象创建”模式 通过“对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。典型模式 Factory MethodAbstract Factory…...
Flask SQLALchemy 的使用
Flask SQLALchemy 的使用 安装 Flask-SQLAlchemy配置 Flask-SQLAlchemy定义模型创建数据库和表插入和查询数据更新和删除数据迁移数据库总结Flask-SQLAlchemy 是一个 Flask 扩展,它简化了 Flask 应用中 SQLAlchemy 的使用。SQLAlchemy 是一个强大的 SQL 工具包和对象关系映射(…...
Metasploit漏洞利用系列(一):MSF完美升级及目录结构深度解读
在信息安全领域,MetasploitFramework(MSF)是一个无处不在的工具,它集合了大量的渗透测试和漏洞利用模块,帮助安全专家识别和利用系统中的弱点。本文将深入探讨如何对Metasploit进行完美升级,以及对其核心目…...
C/C++|经典代码题(动态资源的双重释放与「按值传递、按引用传递、智能指针的使用」)
以下代码中你能看出其存在什么问题?如何修复,能给出几种方法?分别在什么场景下用哪种方法。 #include <iostream>class Buffer {public:Buffer() { std::cout << "Buffer created" << std::endl; }~Buffer() { s…...
西北乱跑娃 -- linux使用笔记
1.后台运行每天一个日志文件 nohup python3.8 manage.py >> $(date %Y-%m-%d).log 2>&1 &2.目录操作: ls:列出目录内容。cd:改变当前工作目录。pwd:显示当前工作目录的路径。mkdir:创建新目录。rmd…...
Kubectl基础命令使用
一.Kubectl 基础命令 格式: kubectl [command] [TYPE] [NAME] [FLAGS] kubectl 是 Kubernetes 的命令行工具,用于管理 Kubernetes 集群。以下是一些常用的 kubectl 命令及其选项: 常用命令 获取资源 列出所有资源类型(Pods、De…...
【ZYNQ】AXI4总线协议实战:从握手时序到PS-PL高效通信
1. AXI4总线协议基础:从握手信号到通道架构 第一次接触ZYNQ的PS-PL通信时,我被AXI4协议里那些VALID/READY信号搞得头晕眼花。直到在示波器上抓到真实的握手波形,才突然理解这个看似复杂的协议其实像极了我们日常的对话机制——只有当说话方准…...
AI驱动博客平台CodeBlog-app:开发者技术分享的智能解决方案
1. 项目概述:一个为开发者而生的AI驱动博客平台最近在GitHub上看到一个挺有意思的开源项目,叫CodeBlog-ai/codeblog-app。光看名字,你可能会觉得这又是一个普通的博客系统,或者是一个AI写作工具。但当我深入去研究它的代码和设计理…...
柔性LED灯丝DIY:从电路原理到创意饰品制作全攻略
1. 项目概述:当生日遇上柔性LED灯丝给孩子的生日派对准备一份独一无二的、会发光的惊喜,是很多家长和手工爱好者的心愿。这次,我们不买现成的塑料灯牌,而是亲手做一个能戴在头上或挂在脖子上的“生日数字灯冠”。这个项目的核心&a…...
FPGA高速ADC数据采集实战——基于AD9253 LVDS接口与ISERDESE2设计
1. AD9253高速ADC核心特性解析 AD9253这颗14位125MSPS四通道ADC芯片,在通信和医疗成像领域堪称经典。我经手过的多个雷达项目中,它的信噪比表现总能带来惊喜——75.3dBFS的实测数据比手册标称值还要稳定。但真正让工程师们又爱又恨的,是它那个…...
使用mcp-maker快速构建AI工具调用服务器:从协议原理到工程实践
1. 项目概述与核心价值最近在折腾AI应用开发,特别是想给大语言模型(LLM)装上更强大的“手脚”,让它能直接操作我电脑上的各种软件和工具。这听起来很酷,对吧?但实际操作起来,你会发现一个核心痛…...
Unity游戏开发集成MCP协议:AI助手自动化操作指南
1. 项目概述:Unity游戏开发中的MCP革命如果你是一名Unity开发者,最近可能已经注意到一个名为“CoderGamester/mcp-unity”的项目在GitHub上悄然走红。这不仅仅是一个普通的插件或工具包,它代表了一种全新的工作流范式,旨在将大型语…...
开源项目容器镜像全流程实践:从命名规范到生产部署
1. 项目概述:从镜像名到开源协作生态的深度解构看到mco-org/mco这个镜像名,很多人的第一反应可能是去 Docker Hub 或 GitHub 上搜索,看看它具体是什么。但今天,我想从一个更本质、更实战的角度来聊聊这个话题。mco-org/mco不是一个…...
C++运行时类型识别实战:从typeid().name()到可读类型名
1. 为什么我们需要关心运行时类型识别? 在C开发中,我们经常会遇到需要知道某个变量或表达式具体类型的情况。特别是在调试复杂代码、编写泛型程序或进行元编程时,能够准确获取类型信息就显得尤为重要。想象一下,当你看到一个日志输…...
从零构建可定制对话系统:架构设计、RAG与智能体实战
1. 项目概述:从零构建一个可定制的对话系统最近在折腾一个挺有意思的东西,我把它叫做“customized-chat”。这名字听起来可能有点泛,但它的核心目标非常明确:打造一个完全由你自己掌控、能深度融入你特定业务逻辑或知识体系的对话…...
基于CircuitPython与加速度计的魔法9号球:嵌入式交互项目实践
1. 项目概述:当硬件遇上玄学,用代码打造你的专属“决策神器”在嵌入式开发的世界里,我们常常与传感器、显示屏和逻辑代码打交道,构建着一个个解决实际问题的智能设备。但谁说硬件项目就一定要严肃刻板?今天,…...

