Spring Boot + Vue 跨域配置(CORS)问题解决历程
在使用 Spring Boot 和 Vue 开发前后端分离的项目时,跨域资源共享(CORS)问题是一个常见的挑战。接下来,我将分享我是如何一步步解决这个问题的,包括中间的一些试错过程,希望能够帮助到正在经历类似问题的你。
1. 问题描述
在我们开发的过程中,Vue 前端需要与 Spring Boot 后端通信。如果后端没有正确配置 CORS,浏览器会进行跨域检查并阻止请求,报错信息如下:
Access to XMLHttpRequest at 'http://localhost:8789/auth/register' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
2. 解决方案概述
为了解决这个问题,我们需要在 Spring Boot 应用中配置 CORS。这个过程包括创建一个 CORS 配置类,并在 Spring Security 配置类中应用这个配置。
3. 试错过程
3.1 初步尝试:简单的 CORS 配置
我首先尝试在 Spring Boot 中添加一个简单的 CORS 配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@Configuration
public class CorsConfig {@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.addAllowedOrigin("*");configuration.addAllowedMethod("*");configuration.addAllowedHeader("*");configuration.setAllowCredentials(true);configuration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}
然后,在 WebSecurityConfig
中应用这个配置:
http.cors().configurationSource(corsConfigurationSource());
结果,前端依旧报错,没有任何变化。
3.2 细化 Security 配置
我接着尝试在 WebSecurityConfig
中进一步细化 CORS 配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable();}@BeanCorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.addAllowedOrigin("*");configuration.addAllowedMethod("*");configuration.addAllowedHeader("*");configuration.setAllowCredentials(true);configuration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}
然而,前端还是无法正常发起跨域请求,这让我非常困惑。
3.3 尝试代理配置
为了确保开发过程中跨域请求能正确代理到后端,我在 Vue 项目中添加了代理配置:
首先,确保项目使用 vue-cli
创建,并确保有 vue.config.js
文件。然后添加如下代理配置:
let proxyObj = {};
proxyObj['/'] = {target: 'http://localhost:8789/',changeOrigin: true,pathRewrite: {'^/': ''}
}module.exports = {devServer: {open: true,host: 'localhost',port: 8081,proxy: proxyObj,},
}
这种配置可以使前端的跨域请求通过代理转发到后端。不过,这只是开发环境下的解决方案,并没有真正解决后端的 CORS 配置问题。
3.4 最终解决方案:完善的 CORS 和 Security 配置
经过几次尝试和查阅资料后,我最终找到了一个有效的解决方案,结合之前的经验,创建了一个完善的 CORS 和 Security 配置。
CorsConfig.java
package cn.techfanyi.fanyi.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@Configuration
public class CorsConfig {@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration corsConfig = new CorsConfiguration();corsConfig.addAllowedOriginPattern("*"); // 允许任何源corsConfig.addAllowedMethod("*"); // 允许任何HTTP方法corsConfig.addAllowedHeader("*"); // 允许任何HTTP头corsConfig.setAllowCredentials(true); // 允许证书(cookies)corsConfig.setMaxAge(3600L); // 预检请求的缓存时间(秒)UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfig); // 对所有路径应用这个配置return source;}
}
WebSecurityConfig.java
package cn.techfanyi.fanyi.config;import cn.techfanyi.fanyi.filter.JwtRequestFilter;
import cn.techfanyi.fanyi.security.CustomAccessDeniedHandler;
import cn.techfanyi.fanyi.security.CustomAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {private final JwtRequestFilter jwtRequestFilter;private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;private final CustomAccessDeniedHandler customAccessDeniedHandler;public WebSecurityConfig(JwtRequestFilter jwtRequestFilter,CustomAuthenticationEntryPoint customAuthenticationEntryPoint,CustomAccessDeniedHandler customAccessDeniedHandler) {this.jwtRequestFilter = jwtRequestFilter;this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;this.customAccessDeniedHandler = customAccessDeniedHandler;}@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf().disable().cors(cors -> cors.configurationSource(corsConfigurationSource())).authorizeRequests(authorizedRequests ->authorizedRequests.requestMatchers("/**").permitAll().anyRequest().authenticated()).exceptionHandling(exceptionHandling ->exceptionHandling.authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(customAccessDeniedHandler)).sessionManagement(sessionManagement ->sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}private CorsConfigurationSource corsConfigurationSource() {return new CorsConfig().corsConfigurationSource();}
}
但是又出现以下错误:
java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
这个错误信息表明,在 Spring Boot 的 CORS 配置中,当 allowCredentials
设置为 true
时,allowedOrigins
不能包含特殊值 "*"
, 因为浏览器不允许在 Access-Control-Allow-Origin
响应头中设置 "*"
, 同时还允许凭证(如 cookies)。此时应该使用 allowedOriginPatterns
来代替 allowedOrigins
。
具体的错误原因如下:
java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
这意味着当 allowCredentials
设置为 true
时,不能将 allowedOrigins
设置为 "*"
, 因为它不能在响应头中设置 Access-Control-Allow-Origin
为 "*"
, 同时还允许凭证。为了解决这个问题,您需要将 allowedOrigins
改为使用 allowedOriginPatterns
。
修改 CorsConfigurationSource
如下:
@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration corsConfig = new CorsConfiguration();corsConfig.addAllowedOriginPattern("*"); // 使用 allowedOriginPatterns 代替 allowedOriginscorsConfig.addAllowedMethod("*"); corsConfig.addAllowedHeader("*");corsConfig.setAllowCredentials(true);corsConfig.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfig);return source;}
通过以上配置,可以解决 allowCredentials
和 allowedOrigins
中 "*"
冲突的问题,使得您的 Spring Boot 应用可以正确处理跨域请求。
通过以上配置,前端请求终于可以成功与后端通信,CORS 问题不再出现。
4. 为什么要这样修改
在 Spring Security 6 中,安全配置的方式有所变化。与之前版本相比,Spring Security 6 更加灵活和模块化。为了使 CORS 配置生效,我们需要:
- 明确指定 CORS 配置源:在
securityFilterChain
方法中,通过http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
明确指定使用我们自定义的CorsConfigurationSource
。 - 禁用默认的 CSRF 保护:对于大多数 API 项目,特别是无状态的 RESTful 服务,禁用 CSRF 是常见的做法。通过
http.csrf().disable()
来实现。 - 配置异常处理和会话管理:确保我们的应用是无状态的,并且正确处理认证和授权异常。
5. 结果
经过这些配置,前端可以顺利地与后端通信,避免了 CORS 错误。整个过程让我对 CORS 配置有了更深入的理解。
相关文章:
Spring Boot + Vue 跨域配置(CORS)问题解决历程
在使用 Spring Boot 和 Vue 开发前后端分离的项目时,跨域资源共享(CORS)问题是一个常见的挑战。接下来,我将分享我是如何一步步解决这个问题的,包括中间的一些试错过程,希望能够帮助到正在经历类似问题的你…...

Think | 大模型迈向AGI的探索和对齐
注:节选自我于24年初所写的「融合RL与LLM思想探寻世界模型以迈向AGI」散文式风格文章,感兴趣的小伙伴儿可以访问我的主页置顶或专栏收录,并制作了电子书供大家参考,有需要的小伙伴可以关注私信我,因为属于技术散文风格…...

为什么选择在Facebook投放广告?
2024年了你还没对 Facebook 广告产生兴趣?那你可就亏大了! 今天这篇文章,我们会分享它对你扩大业务的好处。要知道,Facebook 广告凭借它庞大的用户群和先进的定位选项,已经是企业主们有效接触目标受众的必备神器。接下…...

10 ARM 体系
10 ARM 体系 ARM体系1、基本概念1.1 常见的处理器1.2 ARM7三级指令流水线1.3 初识PC寄存器 2、 ARM核的七种工作模式3、ARM核七种异常 ARM体系 1、基本概念 1.1 常见的处理器 PowerPC处理器:飞思卡尔MPC系列 DSP:TI达芬奇系列 FPGA:Xilinx赛灵思的ZYN…...
ubuntu中设置开机自动运行的(sudo)指令
ubuntu版本:22.04.4 在Ubuntu中设置开机自动运行某一条(需要sudo权限的)指令,我们可以通过编辑系统的启动脚本来实现: 创建一个新的启动脚本:创建一个新的脚本文件,并将其放置在 /etc/init.d/ 目…...

删掉Elasticsearch6.x 的 .security-6索引会怎么样?
背景 玩了下 Elasticsearch 的认证,启动 ES 并添加认证后,看到索引列表额外多了一个 .security-6 。以为是没用的,手欠就给删掉了,然后 Elasticsearch 就访问不了了。 只好再重新部署,再看索引内容,发现这…...

Navicat Premium15 下载与安装(免费版)以及链接SqlServer数据库
转自:https://blog.csdn.net/m0_75188141/article/details/139842565...

Vue3配置vite.config.js代理解决跨域问题
前言: 当浏览器发出一个请求时,只要请求URL的协议、域名、端口三者之间任意一个与当前页面URL不同,就称为跨域。 跨域一般出现在开发阶段,由于线上环境前端代码被打包成了静态资源,因而不会出现跨域问题,这篇文章主要给大家介绍了关于Vue3配置vite.config.js解决跨域问题的相…...
Solidity面试题,由浅入深
Solidity是Ethereum智能合约的主要编程语言,面试题的设计旨在评估候选人对Solidity语言特性的掌握程度,以及他们对区块链和智能合约的理解。下面列出了一些常见的Solidity面试题,涵盖基础知识到高级概念,并简要说明每个问题的答案…...

变量的注意或许需要调试
输入一个自然数N(1<N<9),从小到大输出用1~N组成的所有排列,也就说全排列。例如输入3则输出 123 132 213 231 312 321 输入格式: 输入一个自然数N(1<N<9) 输出格式: N的全排列,每行一…...

C# 增删改查教程 代码超级简单
目录 一.留言 二 .帮助类 三 .增删改查代码展示 一.留言 大家好,前几篇文章我们更新了 C# 三层架构的相关代码,主要写了登录,以及增删改查的相关代码,用的三层架构的框架,那么本篇文章一次性更新C#的增删改查相关代…...
OceanBase V4.2特性解析:OB Oracle模式下的 SDO_GEOMETRY 空间数据类型
1. 背景 1.1. SDO_GEOMETRY的应用场景及能力 在数字化城市、物联网和新能源汽车等领域蓬勃发展的背景下,空间数据类型的存储和分析需求日益增长;对于涉及位置信息服务和地理位置信息应用而言,数据库中具备对sdo_geometry数据类型的支持无疑…...
简介面向对象的封装、继承、多态和抽象
面向对象(Object-Oriented)的特点通常归纳为四个核心概念:封装、继承、多态和抽象。 1. 封装(Encapsulation) 定义: 封装是将对象的属性(数据)和方法(操作)打包在一起&…...

OpenCV + CUDA + cuDNN模块编译
简介 在追求高端性能与资源优化并重的应用场景中,如边缘计算设备或资源受限的开发板上运行YOLO等复杂深度学习模型,采用C结合OpenCV与GPU加速技术相较于传统的Python环境展现出显著优势。这种策略不仅极大地提升了执行效率,还显著降低了运行时…...

Redis 缓存预热、雪崩、穿透、击穿
缓存预热 缓存预热是什么 缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!解决方案 使用 PostConstr…...

仿RabbiteMq简易消息队列基础篇(gtest的使用)
TOC gtest介绍 gtest是google的一个开源框架,它主要用于写单元测试,检查自己的程序是否符合预期行为。可在多个平台上使用(包含Linux,MAC OC,Windows等)。它提供了丰富的断言,致命和非致命失败…...

图像处理中的图像梯度和幅值是什么???(通俗讲解)
在边缘检测和特征提取等任务中,图像的梯度和幅值是图像处理中非常重要的概念。 目录 一、图像的梯度1.1 专业解释1.2 通俗理解1.3 计算方式 二、梯度的幅值2.1 专业解释2.2 通俗理解2.3 计算方式 一、图像的梯度 1.1 专业解释 图像的梯度可以看作是图像中亮度或颜…...
01.计算机网络导论
引言 协议分层 协议分层使我们可以将大任务化简成几个更小、更简单的任务。模块化指的是独立的协议层。一个协议层(模块)可以定义为一个具有输入和输出而不需要考虑输入是如何变成输出的黑匣子。当向两台机器提供相同输入得到相同输出时,它…...

API网关:SpringCloud GateWay
一. 网关的作用及背景 1.API网关的作用 请求路由 在我们的系统中由于同一个接口新老两套系统都在使用,我们需要根据请求上下文将请求路由到对应的接口。 统一鉴权 对于鉴权操作不涉及到业务逻辑,那么可以在网关层进行处理,不用下层到业务…...
【Leetcode 383】赎金信 —— 哈希表 有注解
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以,返回 true ;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1: 输入&#…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...