当前位置: 首页 > 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 配置有了更深入的理解。

http://www.lryc.cn/news/422905.html

相关文章:

  • Think | 大模型迈向AGI的探索和对齐
  • 为什么选择在Facebook投放广告?
  • 10 ARM 体系
  • ubuntu中设置开机自动运行的(sudo)指令
  • 删掉Elasticsearch6.x 的 .security-6索引会怎么样?
  • Navicat Premium15 下载与安装(免费版)以及链接SqlServer数据库
  • Vue3配置vite.config.js代理解决跨域问题
  • Solidity面试题,由浅入深
  • 变量的注意或许需要调试
  • C# 增删改查教程 代码超级简单
  • OceanBase V4.2特性解析:OB Oracle模式下的 SDO_GEOMETRY 空间数据类型
  • 简介面向对象的封装、继承、多态和抽象
  • OpenCV + CUDA + cuDNN模块编译
  • Redis 缓存预热、雪崩、穿透、击穿
  • 仿RabbiteMq简易消息队列基础篇(gtest的使用)
  • 图像处理中的图像梯度和幅值是什么???(通俗讲解)
  • 01.计算机网络导论
  • API网关:SpringCloud GateWay
  • 【Leetcode 383】赎金信 —— 哈希表 有注解
  • Linux 常见的冷知识集锦
  • 【喜报】科大睿智祝贺青岛海信网络科技通过CMMI5级评估
  • 2024全国大学生电子设计大赛全国初赛 E题 三子棋游戏装置 一等奖满分最简方案
  • 尚品汇-ES(三十一)
  • NC 跳台阶
  • linux 文件编程
  • 【后端速成 Vue】实现动态表白墙
  • 【日常开发】 java返回ECharts数据结构封装
  • Table表格控件实现单选功能
  • AI技术加速落地 港科广联手思谋打开智能缺陷检测新纪元
  • Python爬虫开发:BeautifulSoup、Scrapy入门