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

news2024/11/15 8:23:48

文章目录

  • 一、概述
    • 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 Flask

app = 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 CORS


app = 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.coloradmin.cn/o/1397863.html

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

相关文章

多维时序 | Matlab实现CNN-LSTM-Mutilhead-Attention卷积长短期记忆神经网络融合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现CNN-LSTM-Mutilhead-Attention卷积长短期记忆神经网络融合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现CNN-LSTM-Mutilhead-Attention卷积长短期记忆神经网络融合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效果…

Qt —— 编译Qt5版本QFTP库,并实现连接服务、获取列表、上传、下载、删除文件等操作(附源码、附基于Qt5编译好的QFTP库)

示例效果1 示例效果2 介绍 QFTP是Qt4的库,Qt5改用了QNetworkAccessManager来代替。但是Qt5提供的QNetworkAccessManager仅支持FTP的上传和下载,所以只能将QFTP库编译为Qt5的库来进行调用。 QFTP在Github的下载地址:https://github.com/qt/qtftp 客户端源码生成的release结果…

安捷伦AgilentE8363B网络分析仪

安捷伦AgilentE8363B网络分析仪 E8363B 是 Agilent 的 40 GHz 网络分析仪。网络分析仪是一种功能强大的仪器&#xff0c;可以以无与伦比的精度测量射频设备的线性特性。许多行业使用网络分析仪来测试设备、测量材料和监控信号的完整性 附加功能&#xff1a; 104 dB 的动态范围…

基于 Redis 实现高性能、低延迟的延时消息的方案演进

1、前言 随着互联网的发展&#xff0c;越来越多的业务场景需要使用延迟队列。比如: 任务调度:延时队列可以用于任务调度&#xff0c;将需要在未来某个特定时刻执行的任务放入队列中。消息延迟处理: 延时队列可以用于消息系统&#xff0c;其中一些消息需要在一段时间后才能被消…

beego的控制器Controller篇 — 错误处理

1 错误处理 在做 Web 开发的时候&#xff0c;经常需要页面跳转和错误处理&#xff0c;beego 这方面也进行了考虑&#xff0c;通过 Redirect 方法来进行跳转&#xff1a; func (this *AddController) Get() {this.Redirect("/", 302) } 如何中止此次请求并抛出异常…

基于ORB算法的图像匹配

基础理论 2006年Rosten和Drummond提出一种使用决策树学习方法加速的角点检测算法&#xff0c;即FAST算法&#xff0c;该算法认为若某点像素值与其周围某邻域内一定数量的点的像素值相差较大&#xff0c;则该像素可能是角点。 其计算步骤如下&#xff1a; 1&#xff09;基于F…

Java 数组、二维数组、值传递和引用传递的区别

数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。 即&#xff1a;数(数据)组(一组)就是一组数据。 数组是一种数据结构&#xff0c; 用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。 数组的定义 在声明数组变量时&…

Unity—配置lua环境变量+VSCode 搭建 Lua 开发环境

每日一句&#xff1a;保持须臾的浪漫&#xff0c;理想的喧嚣&#xff0c;平等的热情 Windows 11下配置lua环境变量 一、lua-5.4.4版本安装到本地电脑 链接&#xff1a;https://pan.baidu.com/s/14pAlOjhzz2_jmvpRZf9u6Q?pwdhd4s 提取码&#xff1a;hd4s 二、高级系统设置 此电…

削峰填谷与应用间解耦:分布式消息中间件在分布式环境下并发流量控制的应用

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;18&#xff09;篇&#xff0c;也是流量控制系列的第&#xff08;4&#xff09;篇。点击上方关注&#xff0c;深入了解支付系统的方方面面。 本篇重点讲清楚分布式消息中间件的特点&#xff0c;常见消息中间件…

二叉树的基础概念及遍历

二叉树(Binary Tree)的基础 1、树的概念 1、树的概念 树是一种非线性的数据结构&#xff0c;是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合&#xff0c;将它称为树&#xff0c;是因为在形状上像一颗倒着的树&#xff0c;如下图所示就是一颗二叉…

Linux网络 ---- PXE高效批量网络装机

目录 一、系统装机 1、系统装机方式 2、系统装机的三种引导方式 3、系统安装过程 二、PXE原理和概念 1、PXE概述 2、搭建PXE网络体系所需条件 3、PXE批量部署的优点 4、PXE通常涉及以下几个组件&#xff1a; 5、PXE实现过程详细 三、搭建过程 1、搭建过程服务详解 …

【SpringBoot】SpringBoot 项目初始化方法

github 搜索 springboot 模板 github 搜索 springboot 模板&#xff0c;拉取现成代码。 SpringBoot 官方的模板生成器 SpringBoot 官方的模板生成器&#xff08;https://start.spring.io/&#xff09; 在 IDEA 开发工具中生成 这里我修改成阿里的镜像主要是要使用 Java8。 …

分库分表场景分析

背景&#xff1a;目前需支撑交易表日五千万数据&#xff0c;后续完全切量到此新系统 数据库&#xff1a;四个部署在Aix系统上的Oracle库、每个库一张交易主表(按日31个物理分区)、十二个交易历史表&#xff08;无分区&#xff09; 服务节点&#xff1a;每个Oracle库都对应着多…

Git 配置与理解

简述 Git 在 Windows 和 Ubuntu 中的配置&#xff0c;以及对 Git 工作区域划分和 Git 中对于文件状态划分的理解。 git 基础安装与配置 基于 WSL 的 Ubuntu 下的 git 打开或关闭Windows功能 -> Hyper-V、Virtual Machine Platform、Windows Subsystem for Linux # 1.必须…

2023 IoTDB Summit:湖南大唐先一科技有限公司主任架构师舒畅《IoTDB 在发电领域的应用实践》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

高清网络视频监控系统技术方案

目 录 一、概述 二、建设目标及需求 &#xff08;一&#xff09;建设总目标 &#xff08;二&#xff09;需求分析 三、设计依据与设计原则 &#xff08;一&#xff09;设计依据 &#xff08;二&#xff09;设计原则 四、建设方案设计 &#xff08;一&…

浪花 - 用户信息展示+更新

1. 用户登录获取登录凭证 已登录的用户才能获取个人信息发送 Aixos 请求登录 const user ref();onMounted(async () > {const res await myAxios.get(/user/current);if (res.code 0) {console.log("获取用户信息成功");user.value res.data;} else {consol…

进入docker容器,vi: command not found

问题描述&#xff1a; 进入docker容器&#xff0c;查看文件执行vim 命令&#xff0c;报错 vim: command not found。搜索解决方案&#xff0c;说执行一下 apt-get install vim命令&#xff0c;然后又报错 Unable to locate package vim。 解决&#xff1a; 1.执行 npt-get up…

亚马逊云科技 WAF 部署小指南(六)追踪 Amazon WAF Request ID,排查误杀原因

众所周知&#xff0c;中国是全球制造业的巨大力量&#xff0c;许多中国企业通过 2B 电商平台网站进行商品销售和采购。在这些电商平台上&#xff0c;Web 应用防火墙&#xff08;WAF&#xff09;成为不可或缺的安全工具。然而&#xff0c;WAF 也可能导致误杀问题。一旦误杀发生&…

Deep MultimodalLearningA survey on recent advances and trends

深度多模态学习&#xff1a;对近期进展和趋势的综述 深度学习的成功已经成为解决越来越复杂的机器学习问题的催化剂&#xff0c;这些问题通常涉及多个数据模态。我们回顾了深度多模态学习的最新进展&#xff0c;并突出了该活跃研究领域的现状&#xff0c;以及存在的差距和挑战…