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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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


相关文章
|
23天前
|
JavaScript Java 程序员
SpringBoot自动配置及自定义Starter
Java程序员依赖Spring框架简化开发,但复杂的配置文件增加了负担。SpringBoot以“约定大于配置”理念简化了这一过程,通过引入各种Starter并加载默认配置,几乎做到开箱即用。
81 10
SpringBoot自动配置及自定义Starter
|
1月前
|
Java Maven Spring
SpringBoot配置跨模块扫描问题解决方案
在分布式项目中,使用Maven进行多模块开发时,某些模块(如xxx-common)没有启动类。如何将这些模块中的类注册为Spring管理的Bean对象?本文通过案例分析,介绍了两种解决方案:常规方案是通过`@SpringBootApplication(scanBasePackages)`指定扫描路径;推荐方案是保持各模块包结构一致(如com.xxx),利用SpringBoot默认扫描规则自动识别其他模块中的组件,简化配置。
SpringBoot配置跨模块扫描问题解决方案
|
1月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
119 14
|
2月前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
1月前
|
安全 Java 应用服务中间件
SpringBoot:CORS是什么?SpringBoot如何解决跨域问题?
CORS是Web开发中常见且重要的机制,SpringBoot通过提供注解、全局配置和过滤器等多种方式来解决跨域问题。选择适合的方式可以帮助开发者轻松处理跨域请求,提高应用的灵活性和安全性。
143 2
|
2月前
|
前端开发 安全 Java
springboot解决跨域问题
跨域问题指前端调用与后端接口不在同一域名或端口时产生的安全限制。本文介绍两种在Spring Boot中解决跨域问题的方法:一是通过配置CorsFilter,二是实现WebMvcConfigurer接口。配置完成后重启项目即可生效。作者:博笙困了。来源:稀土掘金。
|
2月前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
115 2
|
2月前
|
安全 JavaScript Java
SpringBoot解决跨域最佳实践
本文介绍了跨域问题的起因及最佳实践,重点讲解了SpringBoot中如何利用`CorsFilter`解决跨域问题。首先解释了由于浏览器的同源策略限制导致的跨域现象,然后提出了在服务端入口处解决跨域问题的建议,最后详细展示了三种SpringBoot中配置跨域的方法:使用默认配置、自定义配置规则以及通过配置文件管理跨域设置,以适应不同的应用场景。
|
SQL 关系型数据库 MySQL
SpringBoot自定义配置注入的方式:自定义配置文件注入,从mysql读取配置进行注入
SpringBoot自定义配置注入的方式:自定义配置文件注入,从mysql读取配置进行注入
339 0
|
8月前
|
Java 数据库连接 Maven
SpringBoot【付诸实践 01】SpringBoot自定义starter保姆级教程(说明+源码+配置+测试)
SpringBoot【付诸实践 01】SpringBoot自定义starter保姆级教程(说明+源码+配置+测试)
82 1

推荐镜像

更多