解决系统开发中的跨域问题:CORS、JSONP、Nginx

文章目录

  • 一、概述
    • 1.问题场景
    • 2.浏览器的同源策略
    • 3.解决思路
  • 二、一点准备工作
    • 1.创建前端工程1
    • 2.创建后端工程
    • 3.创建前端工程2
    • 4.跨域问题
  • 三、方法1:使用CORS
  • 四、方法2:JSONP
  • 五、方法3:Nginx
    • 1.安装和启动(windows)
    • 2.使用Nginx配置转发规则
    • 3.修改后端工程的ip
    • 4.前端代码修改

本博客配套的源码在这里

一、概述

1.问题场景

最近我在做一个系统的全栈开发,遇到了这样一个问题。

首先,我的前端是一个来自百度的开源框架——Amis,它封装自React.js,基于JSON配置。我下载了Amis提供的SDK文件夹,并进行了代码开发。但是我在部署整个系统的时候遇到了跨域问题。原因是,我的前端不是以服务的形式运行的,它是一组在浏览器中打开的HTML页面。

如果我在浏览器中打开一个HTML页面,当前采用的协议通常是HTTP或HTTPS,域名通常是"localhost"或者是HTML文件所在的服务器的域名,端口通常是80(HTTP)或443(HTTPS)。由于我是通过文件路径直接打开HTML文件,那么协议、域名和端口都是本地文件系统的相关信息。

而我需要在前端中调用后端的接口,尽管后端IP与前端一致,但PORT和前端不同,因此在浏览器中访问系统时,触发了浏览器的同源策略,导致我的前端无法访问后端接口。

2.浏览器的同源策略

在这里插入图片描述

根据同源策略,浏览器会阻止页面中的JavaScript代码向不同域名、协议或端口的资源发出跨域请求。这意味着如果我的HTML页面和后端服务的域名、协议或端口不一致,浏览器会阻止这种跨域请求。

3.解决思路

我上网查阅了资料,发现,在遵守同源策略的前提下,可以采取以下方法来实现前端页面对后端服务的访问:

  1. 使用CORS(跨域资源共享):在后端服务中配置允许特定域名的跨域请求,通过设置响应头来允许跨域访问。这样可以让前端页面在浏览器中向后端服务发出跨域请求。
  2. JSONP(JSON with Padding):在一些旧版浏览器中,可以通过JSONP来进行跨域请求。不过需要注意JSONP存在一些安全性方面的问题,需要谨慎使用。
  3. 代理服务器:在开发环境中,可以设置代理服务器来转发前端请求到后端服务,使得前端页面和后端服务在同一个域名下,从而避免跨域问题。

下面我将对这三种方法进行实践。

二、一点准备工作

1.创建前端工程1

前端工程1我写了一个HTML页面,放在HTML目录下。在点击按钮后,显示弹窗,如果后端顺利返回,则将返回的文本显示到弹窗上;如果发生异常,在弹窗上显示异常信息。

home.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>调用后端服务</title>
</head>
<body><h1>调用后端服务示例</h1><button id="getDataBtn">获取数据</button><script>document.getElementById('getDataBtn').addEventListener('click', function() {fetch('http://127.0.0.1:2020/api/hello').then(response => {if (!response.ok) {throw new Error('Network response was not ok');}return response.text();}).then(data => {alert(data);}).catch(error => {alert('发生错误: ' + error.message);});});</script>
</body>
</html>

2.创建后端工程

我创建了一个Flask项目,因为Flask足够的简单、快捷,但是你也可以使用任何你熟悉的语言和框架。

使用之前需要本地有python环境,并执行pip install flask来安装依赖,并在我提供的源码的FLASK目录下执行python app.py来运行项目。

app.py

from flask import Flaskapp = Flask(__name__)@app.route('/api/hello', methods=['GET'])
def get_data():return jsonify(data='hello,flask!')if __name__ == '__main__':app.run(host='127.0.0.1', port=2020,debug=True)

3.创建前端工程2

我想找到不同形式的前端对应的跨域问题的解决方案,因此我创建了两种前端,除了上面的HTML页面的形式,还包括了服务的形式。

我创建了一个vue.js脚手架项目,并在里面写了和前端工程1类似的代码。关于怎么快速上手vue,可以看我的另一篇博客:这里

在VUE/vue-app目录下执行npm run serve运行项目。

App.vue

<template><div><button @click="getData">获取数据</button><div v-if="showModal" class="modal"><div class="modal-content"><span v-if="responseData">{{ responseData }}</span><span v-if="error">{{ error }}</span></div></div></div>
</template><script>
export default {data() {return {showModal: false,responseData: null,error: null};},methods: {getData() {fetch('http://127.0.0.1:2020/api/hello').then(response => {if (!response.ok) {throw new Error('Network response was not ok');}return response.text();}).then(data => {this.responseData = data;this.showModal = true;}).catch(error => {this.error = '发生错误: ' + error.message;this.showModal = true;});}}
};
</script><style>
.modal {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center;
}.modal-content {background-color: white;padding: 20px;border-radius: 5px;
}
</style>

4.跨域问题

  • 前端工程1——HTML:

在浏览器中打开home.html页面,点击按钮后,无法成功获取后端数据,触发了浏览器的同源策略。

在这里插入图片描述

  • 前端工程2——VUE:

在这里插入图片描述

下面就来着手解决这个问题。

三、方法1:使用CORS

在后端服务中配置允许特定域名的跨域请求,通过设置响应头来允许跨域访问。这样可以让前端页面在浏览器中向后端服务发出跨域请求。

在 Flask 项目中应用 CORS(app) 的底层原理涉及到在 HTTP响应中添加特定的头部信息,以允许跨域请求访问资源。Flask-CORS 扩展简化了这个过程,它通过在响应中添加适当的 CORS头部信息来实现跨域资源共享。 具体来说,当你在 Flask 项目中调用 CORS(app) 时,Flask-CORS会自动为你的应用程序添加 CORS 头部信息,包括Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 等。这些头部信息告诉浏览器哪些跨域请求是被允许的,从而解决了跨域请求被浏览器阻止的问题。
Flask-CORS 简化了这个过程。

这种方法只能对前端工程是服务(前端工程2)的情况生效,原因就是后端需要配置前端的ip、端口等信息,而前端工程1是以文件的形式打开前端页面并访问后端的。

当以文件路径的形式在浏览器中打开HTML页面文件时,页面中调用后端的API时,无法直接获取调用者的IP和端口。这是因为以文件路径形式打开HTML页面时,页面的请求是直接从文件系统发出的,而不是通过网络协议进行通信,因此无法获取调用者的IP和端口信息。

如果需要获取调用者的IP和端口信息,需要将HTML页面部署到一个服务器上,然后通过服务器地址访问页面,这样页面中的请求就会通过网络协议进行通信,从而可以获取调用者的IP和端口信息。

  • 后端配置CORS:
from flask import Flask,request
from flask_cors import CORSapp = Flask(__name__)# 配置前端vue的ip、端口
CORS(app, resources={r"/api/hello": {"origins": "http://127.0.0.1:8080"}})@app.route('/api/hello', methods=['GET'])
def get_data():return jsonify(data='hello,flask!')if __name__ == '__main__':app.run(host='127.0.0.1', port=2020,debug=True)
  • 在浏览器中访问vue页面:

在这里插入图片描述

成功访问到了后端!

四、方法2:JSONP

JSONP(JSON with Padding)是一种利用<script>标签的跨域技术,它允许在不受同源策略限制的情况下从其他域中获取数据。

底层原理是利用 <script> 标签的跨域特性来实现跨域请求。JSONP是一种在客户端与服务器之间进行跨域数据传输的技术,它允许从其他域中获取数据,绕过了浏览器的同源策略限制。 JSONP的基本原理是通过在页面上动态创建一个 <script> 标签,该标签的 src 属性指向包含 JSON 数据的 URL 地址。这个 URL地址会将 JSON 数据包裹在一个函数调用中,这个函数是在客户端预先定义好的。服务器返回的数据会被当做 JavaScript代码执行,从而触发客户端预先定义的函数,实现对数据的处理和展示。

需要注意的是,JSONP存在一些安全性方面的问题,主要是潜在的跨站脚本攻击(XSS)风险。因为JSONP是通过动态创建<script>标签来获取数据的,所以如果被恶意注入了恶意代码,就有可能导致安全问题。因此,在使用JSONP时需要谨慎处理返回的数据,确保数据的安全性。

  • 修改前端工程1的代码:

home.html:

<!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>调用后端服务</title>
</head>
<body><h1>使用JSONP</h1><div id="result"></div><button id="getDataBtn">获取数据</button><script>document.getElementById('getDataBtn').addEventListener('click', function() {var script = document.createElement('script');script.src = 'http://127.0.0.1:2020/api/hello?callback=handleData';document.body.appendChild(script);});function handleData(data) {document.getElementById('result').innerText = data.result;}</script></body>
</html>

失败!触发了浏览器的CORB策略!

在这里插入图片描述

这个错误表明浏览器使用了CORB(Cross-Origin Read Blocking)机制来阻止跨域读取。CORB是一种安全机制,用于防止恶意网站从跨域响应中读取敏感数据。浏览器对JSONP请求进行了CORB阻止。JSONP本身存在安全风险,因为它是通过动态创建<script>标签来获取数据的,这可能导致恶意网站注入恶意代码。因此,浏览器会对JSONP请求进行CORB阻止。

五、方法3:Nginx

在开发环境中,可以设置代理服务器来转发前端请求到后端服务,使得前端页面和后端服务在同一个域名下,从而避免跨域问题。我以Nginx为例:

Nginx 解决跨域问题的底层原理主要是通过设置 HTTP 响应头来实现跨域资源共享(CORS)。当浏览器发起跨域请求时,会先发送一个OPTIONS 预检请求,以确定是否允许跨域访问。Nginx 可以通过设置响应头来响应这个预检请求,从而允许跨域访问。
具体来说,Nginx 可以通过设置 add_header 指令来添加Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers等 CORS 相关的响应头,允许特定域名的跨域请求访问资源。这样一来,浏览器就能够允许跨域请求的发送和接收,从而解决了跨域问题。

关于Nginx详细的学习和使用可以参考我的这篇博客:Nginx

1.安装和启动(windows)

  1. 下载: 下载 Nginx 的 Windows 版本安装文件(https://nginx.org/en/download.html)。

  2. 解压: 下载完成后,解压缩安装文件到你选择的目录。

  3. 启动: 在解压文件夹中双击nginx.exe就能启动nginx;或者在当前目录下打开终端执行nginx命令。如果一切顺利,在浏览器中输入 http://localhost 将访问 Nginx 的欢迎页面。

  4. 配置文件: nginx.conf 文件位于安装目录下的 conf 文件夹中,可用文本编辑器打开并进行修改。

在这里插入图片描述

2.使用Nginx配置转发规则

这一章我以前端工程1为例,前端工程2是vue服务,执行npm run build对项目打包成静态文件,都存放在了dist目录中,后续nginx配置流程和前端工程1是一样的。

nginx.conf:


#user  nobody;
worker_processes  1;#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;#pid        logs/nginx.pid;events {worker_connections  1024;
}http {include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65;server {listen       8000;server_name  192.168.2.107;# 配置后端工程:# 访问http://192.168.2.107:8000/flask/# 时相当于访问http://192.168.2.107:2020/location /flask/ {proxy_pass http://192.168.2.107:2020/;}# 配置前端工程1:这个要放在最下面# 访问http://192.168.2.107:8000/home.html# 时相当于在浏览器中访问HTML文件夹location / {root   "D:\\0 project\\cors\\HTML";}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}}

在nginx安装目录下执行nginx -s reload刷新配置。

3.修改后端工程的ip

将原来的127.0.0.1改成服务器局域网ip(仅对局域网可见),如果有公网ip是最好的,但是我电脑没配置这个。

if __name__ == '__main__':app.run(host='192.168.2.107', port=2020,debug=True)

4.前端代码修改

home.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Nginx示例</title>
</head>
<body><h1>Nginx示例</h1><button id="getDataBtn">获取数据</button><script>document.getElementById('getDataBtn').addEventListener('click', function() {fetch('http://192.168.2.107:8000/flask/api/hello').then(response => {if (!response.ok) {throw new Error('Network response was not ok');}return response.text();}).then(data => {alert(data);}).catch(error => {alert('发生错误: ' + error.message);});});</script>
</body>
</html>
  • 最后,看看效果:

跨域问题通过Nginx解决了!
在这里插入图片描述

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

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

相关文章

初识VUE

文章目录 Vue是什么1.创建一个Vue实例2.插值表达式{{ }}3.Vue的响应式特性4.开发者工具的安装 Vue是什么 概念&#xff1a;Vue是一个用于构建用户界面的渐进式框架 ①构建用户界面&#xff1a;基于数据渲染出用户看到的界面 ②渐进式&#xff1a;循序渐进 ③ 框架&#xff1…

6.4.4释放音频

6.4.4释放音频 许多Flash动画里的音乐或歌曲非常好听&#xff0c;能不能在没有源文件的情况下把里面的声音文件取出来呢&#xff1f;利用Swf2VideoConverter2可以轻松做到这一点。 1&#xff0e;单击“添加”按钮&#xff0c;在弹出的下拉菜单中选择“添加文件”&#xff0c;…

金蝶云星空表单插件获取单据体数据

文章目录 金蝶云星空表单插件获取单据体数据 金蝶云星空表单插件获取单据体数据 使用标识报错 var thisEntry this.View.Model.DataObject["FEntity"] as DynamicObjectCollection;应该使用实体属性 var thisEntry this.View.Model.DataObject["BillEntry&q…

搜索经典题——填充 9*9矩阵

题目&#xff1a;给定一个九行九列矩阵&#xff0c;填充矩阵元素&#xff0c;要求&#xff1a; 1、每一行每一列&#xff0c;每个小九宫格&#xff08;图片画粗的地方就是&#xff09;不能包含相同元素 2、每一行&#xff0c;每一列&#xff0c;每个小九宫格均会完整出现1-9的数…

【Python爬虫】项目案例讲解,一步步教你爬取淘宝商品数据!

前言 随着互联网时代的到来&#xff0c;人们更加倾向于互联网购物&#xff0c;某宝又是电商行业的巨头&#xff0c;在某宝平台中有很多商家数据&#xff0c;今天带大家使用pythonselenium工具获取这些公开的商家数据 环境介绍&#xff1a; python 3.6pycharmseleniumcsvtime…

vue3 项目中 arguments 对象获取失败问题

问题 在 vue3 项目中 获取到的 arguments 对象与传入实参不符&#xff0c;打印出函数中的 arguments 对象显示如下&#xff1a; 原因 作者仔细回看代码才发现&#xff0c;自己一直用的是 vue3 的组合式写法&#xff0c;函数都是箭头函数&#xff0c;而箭头函数不存在 argumen…

cetos7搭建部署k8s 版本1.28

主机分配 内存最少是4G cpu个数最少两个 IP内存CPU主机名192.168.231.12044K1 192.168.231.12144K2192.168.231.12244K3 关闭防火墙 systemctl stop firewalled 关闭swap vim /etc/fstab 设置主机名称 hostnameset 安装docker 三个主机 初始化集群 在mas…

1432 - 走出迷宫的最少步数-广度优先搜索BFS

代码 #include<bits/stdc.h> using namespace std; char a[51][51]; int r,c; int fx[4]{0,0,1,-1}; int fy[4]{1,-1,0,0}; int tx,ty; struct Node{int x,y,step; }; int bfs(int x,int y){a[x][y]#;queue<Node> q;q.push({x,y,1});while(!q.empty()){Node Curre…

第五篇:其他窗口部件 QAbstractButton

QAbstractButton QAbstractButton 类是按钮部件的抽象基类&#xff0c;提供了按钮的通用功能。它的子类包括标准按钮 QPushButton、工具按钮 QToolButton、复选框 QCheckBox和单选按钮 QRadioButton 等。 QPushButton QPushButton 提供了创建交互按钮的基本功能。它可以包含…

css3+javaScript实现一个左右钟摆-摇晃的红灯笼网页特效

css3javaScript实现一个左右钟摆-摇晃的红灯笼网页特效&#xff01;前天逛博客时无意中看见了&#xff0c;别人的博客顶部有一个会左右钟摆的摇晃的红灯笼&#xff0c;产生了想法&#xff0c;我也想给自己做一个&#xff0c;但是网上找了很多方案&#xff0c;都没有实现。终于在…

论文阅读笔记AI篇 —— Transformer模型理论+实战 (三)

论文阅读笔记AI篇 —— Transformer模型理论实战 &#xff08;三&#xff09; 第三遍阅读&#xff08;精读&#xff09;3.1 Attention和Self-Attention的区别&#xff1f;3.2 Transformer是如何进行堆叠的&#xff1f;3.3 如何理解Positional Encoding&#xff1f;3.x 文章涉及…

【C++入门到精通】智能指针 shared_ptr 简介及C++模拟实现 [ C++入门 ]

阅读导航 引言一、简介二、成员函数三、使用示例四、C模拟实现五、std::shared_ptr的线程安全问题六、总结温馨提示 引言 在 C 动态内存管理中&#xff0c;除了 auto_ptr 和 unique_ptr 之外&#xff0c;还有一种智能指针 shared_ptr&#xff0c;它可以让多个指针共享同一个动…

钉钉副总裁李智勇:AI超级助理,提升大模型时代生产力

微软比尔盖茨此前曾预言:“五年内&#xff0c;每个人都将拥有AI私人助理Agent&#xff0c;Agent将颠覆软件行业 。” 近日以来&#xff0c;在GPT store正式上线点爆情绪之后&#xff0c;无论国内外&#xff0c;Agent都是创业圈里炙手可热的新贵。一场关于Agent创业比拼大赛&am…

S2-04 ESP-IDF开发 : IIC

IIC IIC(Inter&#xff0d;Integrated Circuit)总线是一种由NXP&#xff08;原PHILIPS&#xff09;公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信&#xff0c;在小数据量场合使用&#xff…

【Linux第二课-权限】操作系统、Linux用户、Linux权限、Linux文件类型、粘滞位

目录 操作系统shell外壳为什么有shell外壳shell外壳是什么shell外壳工作原理 Linux用户root用户与非root用户root用户与普通用户的切换普通用户 --> root用户root用户 --> 普通用户普通用户 --> 普通用户对一条指令提升为root权限进行执行 Linux权限Linux中的权限角色…

解决国内Linux服务器无法使用Github的方法

解决思路&#xff1a;修改Host https://www.ipaddress.com/ 利用上面的网站查询github.com和raw.githubusercontent.com的DNS解析的IP地址 最后&#xff0c;修改服务器的/etc/hosts 添加如下两行&#xff1a; 140.82.112.3 github.com 185.199.108.133 raw.githubuserconte…

树形结构下拉框组件vue-treeselect的使用(安装、模糊匹配、单选、多选、延迟加载、异步搜索等)

一、基本使用流程 首先npm安装依赖 npm install riophae/vue-treeselect --save然后在需要使用的组件中引入 import Treeselect from riophae/vue-treeselect import riophae/vue-treeselect/dist/vue-treeselect.css声明组件 components: { Treeselect }使用 <treesele…

安装布隆过滤器

上传并解压文件解压文件 tar -zxvf RedisBloom-2.2.4.tar.gz 进入解压好的文件 make一下 返回进入conf 配置文件 loadmodule /usr/local/etc/redis/redisbloom.so 粘入 拷贝redisbloom.so到容器 : docker cp redisbloom.so redis:/usr/local/etc/redis 重启redis : docker …

NX二次开发获取圆弧的四个象限点

我是用来用来画水路线框的UF_MODL_ask_curve_points&#xff08;&#xff09;可以按弧长或者弧度获取曲线的等分点&#xff0c;取PI/2的圆弧&#xff0c;即将圆弧四等分&#xff0c;你也可以取任意等分点。 int GetArcPoint(tag_t arc_tag,double point[4][3]) {if(arc_tag0)r…

Ubuntu 20.04安装yum报错:E: Unable to locate package yum

直接上解决方案&#xff01; 1、选择自己对应的版本的源地址 注意需要选择跟系统版本一致的&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/ 找到Ubuntu点击小问号&#xff0c;进去选择对应的版本&#xff0c;将下面的镜像复制到Linux系统的 /etc/apt/sourc…