SpringBoot 配置CORS处理前后端分离跨域配置无效问题解析

简介: SpringBoot 配置CORS处理前后端分离跨域配置无效问题解析

前言

浏览器有跨域限制,非同源策略(协议、主机名或端口不同)被视为跨域请求,解决跨域有跨域资源共享(CORS)、反向代理和 JSONP的方式。本篇通过 SpringBoot 的资源共享配置(CORS)来解决前后端分离项目的跨域,以及从原理上去解决跨域配置不生效的问题。


准备工作

使用前后端分离开源项目 youlai-boot + vue3-element-admin 做跨域请求测试 。


其中 vue3-element-admin 默认通过 vite + proxy 前端反向代理解决跨域,如果想关闭方向代理只需修改 baseURL 即可:


// request.ts

const service = axios.create({

 //baseURL: import.meta.env.VITE_APP_BASE_API,  // 前端反向代理解决跨域的配置

 baseURL: "http://localhost:8989", // 后端通过配置CORS解决跨域的配置, http://localhost:8989 是后端接口地址

 timeout: 50000,

 headers: { 'Content-Type': 'application/json;charset=utf-8' }

});

配置 CORS 允许跨域

一般情况在项目添加以下配置即可解决浏览器跨域限制。


/**

* CORS 资源共享配置

*

* @author haoxr

* @date 2022/10/24

*/

@Configuration

public class CorsConfig {

   @Bean

   public CorsFilter corsFilter() {

       CorsConfiguration corsConfiguration = new CorsConfiguration();

       //1.允许任何来源

       corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));

       //2.允许任何请求头

       corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);

       //3.允许任何方法

       corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);

       //4.允许凭证

       corsConfiguration.setAllowCredentials(true);

       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

       source.registerCorsConfiguration("/**", corsConfiguration);

       return new CorsFilter(source);

   }

}


CORS 允许跨域原理

CorsFilter 读取 CorsConfig 配置通过 DefaultCorsProcessor 给 response 响应头添加 Access-Control-Allow-* 以允许跨域请求能够被成功处理。


响应头参数 作用

Access-Control-Allow-Origin 允许访问的源地址

Access-Control-Allow-Methods 允许访问的请求方法

Access-Control-Allow-Headers 允许访问的请求头

Access-Control-Allow-Credentials 是否允许发送 Cookie 等身份凭证

Access-Control-Max-Age 缓存预检请求的时间

核心是 DefaultCorsProcessor# handleInternal 方法

微信图片_20230710102901.png

微信图片_20230710102949.png




CORS 配置失效原理分析

但。。。有的项目按照如上配置允许跨域请求成功了,但有些项目却不生效?


其实就是一个**结论**:有中断响应的过滤器在 CorsFilter 之前执行了,也就无法执行到 CorsFilter,自然 CorsConfiguration 中的配置形同虚设。


常见的场景:项目中使用了 Spring Security 安全框架导致 CORS 跨域配置失效。


接下来就 Spring Security 导致 CORS 配置失效展开分析。


在 ApplicationFilterChain#internalDoFilter 添加断点,然后通过改造后(移除反向代理)的 vue3-element-admin 发出跨域请求。

微信图片_20230710103029.png



可以看出 SpringSecurityFilterChain 是先于 CorsFilter 执行的(重点), 如果是跨域请求浏览器会在正式请求前发出一次预检请求(OPTIONS),判断服务器是否允许跨域。


跨域请求没到达 CorsFilter 过滤器就先被 Spring Security 的过滤器给拦截了,要知道预检 OPTIONS 请求是不带 token 的,所以响应 401 未认证的错误。预检请求失败导致后面的请求响应会被浏览器拦截。

微信图片_20230710103044.png



CORS 配置失效解决方案

根据配置失效原理分析,有两个解决方案:


解决方案一: 配置 CorsFilter 优先于 SpringSecurityFilter 执行;


解决方案二: 放行预检 OPTIONS 请求 + 基础 CORS 配置。


解决方案一(推荐)

配置 CorsFilter 优先于 SpringSecurityFilter 执行


Spring Security 过滤器是通过 SecurityFilterAutoConfiguration 的 DelegatingFilterProxyRegistrationBean 注册到 servletContext上下文,其中过滤器的顺序属性 Order 读取的 是 SecurityProperties 的默认配置也就是 -100;

微信图片_20230710103053.png微信图片_20230710103055.png


SpringBoot 可以通过 FilterRegistrationBean 来对 Filter 自定义注册(排序), 设置 Order 小于 SpringSecurity 的 -100 即可。完整配置如下:


/**

* CORS资源共享配置

*

* @author haoxr

* @date 2023/4/17

*/

@Configuration

public class CorsConfig {

   @Bean

   public FilterRegistrationBean filterRegistrationBean() {

       CorsConfiguration corsConfiguration = new CorsConfiguration();

       //1.允许任何来源

       corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));

       //2.允许任何请求头

       corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);

       //3.允许任何方法

       corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);

       //4.允许凭证

       corsConfiguration.setAllowCredentials(true);

       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

       source.registerCorsConfiguration("/**", corsConfiguration);

       CorsFilter corsFilter = new CorsFilter(source);

       FilterRegistrationBean<CorsFilter> filterRegistrationBean=new FilterRegistrationBean<>(corsFilter);

       filterRegistrationBean.setOrder(-101);  // 小于 SpringSecurity Filter的 Order(-100) 即可

       return filterRegistrationBean;

   }

}


可以看到不同源的跨域请求能够成功响应。

微信图片_20230710103111.png



解决方案二

放行预检 OPTIONS 请求 + 基础 CORS 配置


SecurityConfig 放行 OPTIONS 预检请求配置 SecurityConfig 配置源码


   @Bean

   public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

       http

            ...

               // 走 Spring Security 过滤器链的放行配置

               .requestMatchers(HttpMethod.OPTIONS,"/**").permitAll() // 放行预检请求

               .anyRequest().authenticated();

       return http.build();

   }

   @Bean

   public WebSecurityCustomizer webSecurityCustomizer() {

       // 不走过滤器链的放行配置

       return (web) -> web.ignoring()

               .requestMatchers(HttpMethod.OPTIONS,"/**") // 放行预检请求

     

   }


基础的跨域共享配置


@Configuration

public class CorsConfig {

   @Bean

   public CorsFilter corsFilter() {

       CorsConfiguration corsConfiguration = new CorsConfiguration();

       //1.允许任何来源

       corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));

       //2.允许任何请求头

       corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);

       //3.允许任何方法

       corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);

       //4.允许凭证

       corsConfiguration.setAllowCredentials(true);

       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

       source.registerCorsConfiguration("/**", corsConfiguration);

       return new CorsFilter(source);

   }

 

}


另外有自定义过滤器 (例如:VerifyCodeFilter)通过 response.getWriter().print() 响应给浏览器也是不走后面的 CorsFilter 过滤器,所以需要设置响应头


// ResponseUtils# writeErrMsg

response.setContentType(MediaType.APPLICATION_JSON_VALUE);

response.setHeader("Access-Control-Allow-Origin","*");

response.getWriter().print(JSONUtil.toJsonStr(Result.failed(resultCode)));

1

2

3

4

前/后端源码

完整项目源码地址如下,如果有相关问题可以通过项目 关于我们 添加交流群。


Gitee Github

前端 vue3-element-admin vue3-element-admin

后端 youlai-boot youlai-boot


相关文章
|
18天前
|
安全 Java 开发者
深入理解Spring Boot配置绑定及其实战应用
【4月更文挑战第10天】本文详细探讨了Spring Boot中配置绑定的核心概念,并结合实战示例,展示了如何在项目中有效地使用这些技术来管理和绑定配置属性。
23 1
|
5天前
|
JSON JavaScript Java
SpringBoot读取配置优先级顺序是什么?
Spring Boot的外部配置加载优先级是开发者理解和管理应用程序配置的关键。它支持多种配置源,包括Java属性文件、YAML文件、环境变量、命令行参数等。配置加载顺序从低到高为:默认属性、@PropertySource加载的配置、Config Data(内部配置文件、外部配置文件)、环境变量、系统属性、Servlet容器初始化参数、SPRING_APPLICATION_JSON格式的环境变量或系统属性以及命令行参数。了解这一顺序有助于在不同环境中灵活配置和管理Spring Boot应用,确保其按预期运行。
|
5天前
|
域名解析 缓存 监控
【域名解析 DNS 专栏】解析失败的 DNS 重试策略与配置优化
【5月更文挑战第28天】DNS解析在数字化时代关键但常遇失败,可能由网络、服务器或域名错误引起。实施智能重试策略(如指数级增长的重试间隔)和配置优化(如选用可靠DNS服务器、设置缓存、监控预警)能提高成功率和系统稳定性。示例代码展示基本DNS重试函数,强调需按业务需求调整策略并配合监控以保证高效稳定的DNS解析。
|
7天前
|
Java 数据库连接 网络安全
springboot使用Pivotal Greenplum JDBC如何进行配置
【5月更文挑战第23天】springboot使用Pivotal Greenplum JDBC如何进行配置
31 6
|
9天前
|
Java 应用服务中间件 Maven
SpringBoot概述&SpringBoot基础配置&yml的使用&多环境启动
SpringBoot概述&SpringBoot基础配置&yml的使用&多环境启动
22 2
1天搞定SpringBoot+Vue全栈开发 (9)JWT跨域认证
1天搞定SpringBoot+Vue全栈开发 (9)JWT跨域认证
|
10天前
|
Java Python Spring
小唐开始学 Spring Boot——(2)Spring Boot核心配置与注解
小唐开始学 Spring Boot——(2)Spring Boot核心配置与注解
|
11天前
|
域名解析 存储 缓存
【域名解析DNS专栏】动手实践:手动配置DNS解析记录
【5月更文挑战第22天】本文介绍了DNS解析记录的概念及其手动配置步骤。DNS解析记录是将域名映射到IP地址的数据,常见类型包括A(IPv4)、AAAA(IPv6)和CNAME(别名)。配置步骤包括登录DNS管理平台,添加记录,选择记录类型,填写主机记录和记录值,设置TTL值,并保存。以阿里云为例的A记录配置示例也提供了具体操作。了解这些有助于更好地管理域名。
【域名解析DNS专栏】动手实践:手动配置DNS解析记录
|
13天前
|
JSON 安全 前端开发
跨域详解及Spring Boot 3中的跨域解决方案
本文介绍了Web开发中的跨域问题,包括概念、原因、影响以及在Spring Boot 3中的解决方案。跨域是由浏览器的同源策略限制引起的,阻碍了不同源之间的数据传输。解决方法包括CORS、JSONP和代理服务器。在Spring Boot 3中,可以通过配置CorsFilter来允许跨域请求,实现前后端分离项目的正常运行。
66 3
 跨域详解及Spring Boot 3中的跨域解决方案
|
18天前
|
Java 文件存储 Spring
【springboot】logback配置
【springboot】logback配置
23 1

推荐镜像

更多