当前位置: 首页 > news >正文

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;}

通过以上配置,可以解决 allowCredentialsallowedOrigins"*" 冲突的问题,使得您的 Spring Boot 应用可以正确处理跨域请求。

通过以上配置,前端请求终于可以成功与后端通信,CORS 问题不再出现。

4. 为什么要这样修改

在 Spring Security 6 中,安全配置的方式有所变化。与之前版本相比,Spring Security 6 更加灵活和模块化。为了使 CORS 配置生效,我们需要:

  1. 明确指定 CORS 配置源:在 securityFilterChain 方法中,通过 http.cors(cors -> cors.configurationSource(corsConfigurationSource())) 明确指定使用我们自定义的 CorsConfigurationSource
  2. 禁用默认的 CSRF 保护:对于大多数 API 项目,特别是无状态的 RESTful 服务,禁用 CSRF 是常见的做法。通过 http.csrf().disable() 来实现。
  3. 配置异常处理和会话管理:确保我们的应用是无状态的,并且正确处理认证和授权异常。

5. 结果

经过这些配置,前端可以顺利地与后端通信,避免了 CORS 错误。整个过程让我对 CORS 配置有了更深入的理解。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 构建智能生态,视频监控/安防监控EasyCVR视频汇聚流媒体技术在智能分析领域的应用
  • 《TOGAF®标准第10版》:企业架构新时代的必备指南与实践蓝图
  • JS【详解】 延迟加载
  • 阿里云服务器 ECS部署jenkins
  • 《企业净零排放实用手册》:助力中小企业实现“双碳”目标
  • 工业智能网关在汽车制造企业的应用价值及功能-天拓四方
  • EVAL长度突破限制
  • Golang 并发编程
  • 【相机与图像】2. 相机内外参的标定的代码示例
  • 中国科技统计年鉴,数据覆盖1991-2022年多年份
  • 基于Python大数据的电商产品评论的情感分析设计与实现,包括lda主题分析和情感分析
  • 面经精选:数据库高频面试十问
  • Python 为Excel单元格设置填充\背景色 (纯色、渐变、图案)
  • 【计算机网络——internet结构和ISP】
  • LVS-NAT + LVS-DR
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • k个最大的数及变种小结
  • Linux各目录及每个目录的详细介绍
  • PhantomJS 安装
  • php ci框架整合银盛支付
  • Protobuf3语言指南
  • python学习笔记 - ThreadLocal
  • quasar-framework cnodejs社区
  • React16时代,该用什么姿势写 React ?
  • 代理模式
  • 给github项目添加CI badge
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 想写好前端,先练好内功
  • 协程
  • 用jQuery怎么做到前后端分离
  • 与 ConTeXt MkIV 官方文档的接驳
  • 阿里云重庆大学大数据训练营落地分享
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​人工智能书单(数学基础篇)
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #14vue3生成表单并跳转到外部地址的方式
  • #APPINVENTOR学习记录
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (7)摄像机和云台
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (C++)八皇后问题
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (ZT)一个美国文科博士的YardLife
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (一) 初入MySQL 【认识和部署】
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • .gitignore文件—git忽略文件
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net core 外观者设计模式 实现,多种支付选择
  • .Net Core 微服务之Consul(三)-KV存储分布式锁
  • .NET delegate 委托 、 Event 事件