前后端分离项目,如何解决跨域问题?

跨域问题是前后端分离项目中非常常见的一个问题,举例来说,编程猫学习网站的前端服务跑在 8080 端口下,后端服务跑在 9002 端口下,那么前端在请求后端接口的时候就会出现跨域问题。

403 Forbidden 是HTTP协议中的一个状态码(Status Code),意味着后端服务虽然成功解析了请求,但前端却没有访问该资源的权限。

那怎么解决这个问题呢?通常有两个思路:

  • 前端使用 Nodejs 代理(开发环境下,生产环境下可以用 Nginx 替代)
  • 或者后端开启跨域资源共享

一、关于跨域

跨域对于前后端开发者来说,就像一块狗皮膏药,无论是面试还是开发中,都会经常遇到。

之所以出现跨域问题,是因为浏览器的同源策略,为了隔离潜在的恶意文件,为了防御来自歪门邪道的攻击,浏览器限制了从同一个源加载的文档或脚本与来自另一个源的资源进行交互。

前面我们提到了,前端跑在 8080 端口下,后端跑在 9002 端口下,这种情况就属于不同的源(域名不同,协议不同,端口不同),所以 8080 端口下的前端请求直接访问 9002 端口下的后端接口时就访问失败了。

那正确的打开方式是什么呢?我们前面也提到了,前端使用 Nodejs 代理或者后端开启跨域资源共享,我们一一来实践下。

二、Nodejs 代理

在 Nodejs 出现之前,JavaScript 编写的程序通常需要在用户的浏览器上执行,Node.js 出现后,JavaScript 也能用于服务端编程了。Nodejs 一系列的内置模块使得程序可以脱离 IIS、Apache 这种 Web 服务作为独立的服务器执行。

我们使用 Nodejs 来解决跨域问题的思路就是,在本地创建一个虚拟服务器,对 8080 端口下的前端请求进行代理,同时接收 9002 端口下的服务器端响应,这样服务端和服务端进行数据的交互就不会出现跨域问题了。

第一步,配置 Nodejs 代理服务

module.exports = {dev: {// PathsassetsSubDirectory: 'static',assetsPublicPath: '/',proxyTable: {'/api': {target: 'http://localhost:9002', // 你请求的第三方接口changeOrigin: false, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题pathRewrite: { // 路径重写,'^/api': '' // 替换target中的请求地址,也就是说以后你在请求http://api.codingmore.top/v2/XXXXX这个地址的时候直接写成/api即可。}},},
}
复制代码

第二步,配置前端访问请求路径

module.exports = merge(prodEnv, {NODE_ENV: '"development"',VUE_APP_BASE_API: '"/api"'// VUE_APP_BASE_API: '"http://localhost:9002"'
})
复制代码

第三步,重启前端服务

再次点击「登录」按钮,可以看到请求的 URL 发生了改变,原来是 http://localhost:9002/users/login,现在是 http://localhost:8080/api/users/login。与此同时,可以看到多了一个 Remote Address,端口也是 8080,也就是说经过 Nodejs 的代理,前后端的交互在同一个源下面了,这样就不会发生跨域问题了。

同时,可以看得到,服务器端返回的状态码变成了 200,表示请求成功。

三、开启跨域资源共享

跨域资源共享,也就是 Cross-Origin Resource Sharing,简拼为 CORS,是一种基于 HTTP 头信息的机制,通过允许服务器标识除了它自己以外的资源,从而实现跨域访问。

第一步,开启 CORS 支持

在 Spring Boot 应用中,加入 CORS 的支持简单到不忍直视,添加一个配置类就可以了。

@Configuration
public class GlobalCorsConfig {@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();// 设置你要允许的网站域名config.addAllowedOrigin("http://localhost:8080");//允许跨域发送cookieconfig.setAllowCredentials(true);//放行全部原始头信息config.addAllowedHeader("*");//允许所有请求方法跨域调用config.addAllowedMethod("*");UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}
复制代码

第二步,重启后端服务,再次点击登录按钮,发现请求已经可以正常访问了。

本例中,后端返回 Access-Control-Allow-Origin: http://localhost:8080 就表示,跑在 9002 端口下的后端接口可以被 8080 端口的前端请求访问。

如果允许所有域名进行跨域调用的话,只需改变一行代码即可。

//允许所有域名进行跨域调用
config.addAllowedOriginPattern("*");
// 设置你要允许的网站域名
//        config.addAllowedOrigin("http://localhost:8080");
复制代码

对于 login 这种简单的请求来说,它们是不会触发 CORS 预检的,因此不需要在服务器端增加其他配置就可以了。那什么是简单请求呢?

1)请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

2)HTTP 的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

那对于会触发 CORS 预检的非简单请求(比如说请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是 application/json,或者请求消息头包含了一些自定义的字段),该怎么办呢?

非简单请求在正式通信之前,会增加一次 HTTP 查询请求,称为“预检”请求。预检请求通过后,才会返回正常的响应内容。

拿编程猫的文章管理页来举例,该页面会向后端发起一个 posts/queryPageable 的分页查询,该请求包含了一个自定义的消息头 Authorization,于是浏览器认为该请求是一个非简单请求,然后就会自动发起一次 OPTIONS 请求,但由于我们的 Spring Boot 项目整合了 SpringsScurity 安全管理框架,没有对OPTIONS请求放开登录认证,导致验证失败,文章分页请求的响应数据就没有返回回来。

第三步,通过以下代码给 OPTIONS 请求放行。

public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();//允许跨域请求的OPTIONS请求registry.antMatchers(HttpMethod.OPTIONS).permitAll();}
}
复制代码

再次重启后端服务,重新访问文章列表接口,发现有响应数据了。

非简单请求必须首先使用 OPTIONS 请求方法发起一个预检请求到服务器端,以获知服务器是否允许该实际请求。"预检请求“的使用,避免了跨域请求对服务器的用户数据造成未预期的影响。

我们来通过两张图片简单总结一下预检请求的整个过程,第一张,发起 OPTIONS 预检请求:

第二章,发起正式请求:

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

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

相关文章

华容道问题求解_详细设计(五)之hash值和回放功能

&#xff08;续上文&#xff09; 布局的hash 值计算 笔者也参考了之前的一些文章&#xff0c;很多文章提到了怎么节省存贮空间来查找最优解&#xff0c;这不是笔者的目的。笔者的目的比较单一&#xff0c;就是找到最优解就行了。因此并没有在存贮上面进行过多的优化&#xff…

Linux系统adb调试小米手机调试不成功出现Exception occurred while executing ‘put‘:问题解决

参考文章&#xff1a;执行android settings命令报错原因Exception occurred while executing put: java.lang.SecurityException: Pe... - 简书 (jianshu.com) 解决Android U无法通过adb安装应用(Caller has no access to session -1)的问题_performing streamed install-CSDN…

P3405 [USACO16DEC] Cities and States S题解

题目 Farmer John有若干头奶牛。为了训练奶牛们的智力&#xff0c;Farmer John在谷仓的墙上放了一张美国地图。地图上表明了每个城市及其所在州的代码&#xff08;前两位大写字母&#xff09;。 由于奶牛在谷仓里花了很多时间看这张地图&#xff0c;他们开始注意到一些奇怪的…

消息队列 MQ

文章目录 1. MQ 相关概念1.1 什么是 MQ1.2 为什么要用 MQ1.3 MQ 分类1.4 MQ 的选择 1. MQ 相关概念 1.1 什么是 MQ MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#x…

【 React 】state和props有什么区别?

1. state 一个组件的显示形态可以由数据状态和外部参数所决定&#xff0c;而数据状态就是state&#xff0c;一般在constructor中初始化 当需要修改里面的值的状态需要通过调用setState来改变&#xff0c;从而达到更新组件内部数据的作用&#xff0c;并且重新调用组件render方法…

阿里云DSW做AI绘画时的显卡选择A10?V100?

V100是Volta架构&#xff0c;A10是Ampere架构&#xff0c;架构上讲A10先进点&#xff0c;其实只是制程区别&#xff0c;用起来没区别。 V100是HBM的内存读取&#xff0c;带宽大&#xff0c;但是DDR5的。 二块卡都是全精度为主的算力卡&#xff0c;半精度优势不明显。 需要用…

uniapp 开发app,如何使用模拟器

1、开发app &#xff0c;设置模拟器 &#xff08;uniapp 如何设置模拟器&#xff09; https://blog.csdn.net/sweetsoft/article/details/130727169 2、运行到模拟器 注意&#xff1a;1、模拟器所在的位置&#xff1a;“D:\Program Files\Nox\bin”&#xff0c;在该文件夹下找…

FPN(Feature Pyramid Network)

参考&#xff1a; 【目标检测】FPN(Feature Pyramid Network) - 知乎 Feature pyramid network是CVPR2017年的一篇文章&#xff0c;它在目标检测中融入了特征金字塔&#xff0c;提高了目标检测的准确率&#xff0c;尤其体现在小物体的检测上。 1. 动机(Motivation) 识别不同…

【三两波折】char *foo[]和char(*foo)[]有何不同?

1、先谈优先级 最高级别 —— 有四个&#xff0c;他们并不像运算符&#xff1a; []数组下标左到右结合()用于&#xff08;表达式&#xff09; or 函数名(形参表)左到右结合.读取结构体成员左到右结合->读取结构体成员&#xff08;通过指针&#xff09;左到右结合 第二级别…

【读书笔记】针对ICS的ATTCK矩阵详解(一)

Techniques - ICS | MITRE ATT&CKhttps://attack.mitre.org/techniques/ics/ 一、初始访问&#xff08;Initial Access&#xff09; 该阶段&#xff1a;攻击者正在尝试进入ICS环境。 初始访问包括攻击者可能用作入口向量&#xff0c;从而可以在 ICS 环境中获得初始立足点的…

pytorch实现分割模型TransUNet

TransUNet是一个非常经典的图像分割模型。该模型出现在Transformer引入图像领域的早期&#xff0c;所以结构比较简单&#xff0c;但是实际上效果却比很多后续花哨的模型更好。所以有必要捋一遍pytorch实现TransUNet的整体流程。 首先&#xff0c;按照惯例&#xff0c;先看一下…

逼疯快递员的送货上门,谁来背锅?

快递上门的问题近几年来一直争论不休。 最近&#xff0c;随着新修订的《快递市场管理办法》正式实施&#xff0c;这个话题又成为了焦点。 消费者希望快递能够送上门省去麻烦&#xff0c;快递员希望统一送到代收点提高效率。 是消费者要求太高&#xff1f;快递员太过怠慢&…

LightDB24.1插件oracle_fdw需要支持oracle.date和oracle.varchar2类型

背景介绍 oracle.date和oracle.varchar2是LightDB中新增的类型&#xff0c;对应于Oracle数据库的date和varchar2类型。oracle_fdw是第三方插件&#xff0c;所以不支持oracle.date和oracle.varchar2类型。从LightDB24.1版本开始&#xff0c;oracle_fdw插件开始支持oracle.date和…

理解自相关图AC和偏自相关图PAC Plots

when we talk about the time-series data, many factors affect the time series, but the only thing that affects the lagged version of the variable is the time series data itself. by Yugesh Verma 时序数据按照时间点的先后顺序进行排列,变化是在邻近的时间段之间发…

2.1基本算法之枚举1978:生理周期

人生来就有三个生理周期&#xff0c;分别为体力、感情和智力周期&#xff0c;它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天&#xff0c;人会在相应的方面表现出色。例如&#xff0c;智力周期的高峰&#xff0c;人会思维敏捷&#xff0c;精力容易高…

[金三银四] 系统调用相关

2.36 系统调用的详细流程 Linux 在x86上的系统调用通过 int 0x80 实现&#xff0c;用系统调用号来区分入口函数。操作系统实现系统调用的基本过程是&#xff1a; 应用程序调用库函数&#xff08;API&#xff09;&#xff1b;API 将系统调用号存入寄存器&#xff08;EAX&#…

CKA备考攻略:掌握Pod日志收集,事半功倍的秘诀!

往期精彩文章 : 提升CKA考试胜算&#xff1a;一文带你全面了解RBAC权限控制&#xff01;揭秘高效运维&#xff1a;如何用kubectl top命令实时监控K8s资源使用情况&#xff1f;CKA认证必备&#xff1a;掌握k8s网络策略的关键要点提高CKA认证成功率&#xff0c;CKA真题中的节点维…

稳定性三——wachdog机制与分析发方法

文章目录 1. 介绍2 watchdog 机制2.1 初始化2.2 添加Watchdog监测对象2.3 监测机制 3 问题分析3.1 日志分类3.2 定位3.3 场景还原 4. 实例分析5. 总结 1. 介绍 最早引入Watchdog是在单片机系统中&#xff0c;由于单片机的工作环境容易受到外界磁场的干扰&#xff0c;导致程序“…

uniapp上拉加载、下拉刷新

我这个是自定义header、main、和footer的布局&#xff0c;是盒子中的上拉加载、下拉刷新&#xff0c;不是页面的&#xff0c;废话不说&#xff0c;直接上代码&#xff01; <template><view class"assembly"><u-navbar title"个人中心" lef…

2.JavaWebMySql基础

导语&#xff1a; 一、数据库基本概念 1.什么是数据库 2.关于MySql数据库 二、MySQL的安装与卸载 安装步骤&#xff1a; 卸载步骤&#xff1a; 三、MySQL服务操作 1.服务启动和关闭&#xff1a; 2.登录和退出MySQL&#xff1a; 3.服务自启动&#xff1a; 4.命令行登…