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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 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


相关文章
|
6天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
21天前
|
前端开发 数据可视化 Java
SpringBoot的4中常见入参形式错误解析
在使用SpringBoot进行前后端接口对接时,常遇到如500、400等请求错误,本文总结了四个常见的复杂请求类型及其解决方案,包括实体嵌套List提交、普通文件上传、List提交及数组Array提交,详细展示了正确的前端与后端代码实现,帮助开发者避免常见错误,提高开发效率。
27 0
SpringBoot的4中常见入参形式错误解析
|
20天前
|
持续交付 jenkins Devops
WPF与DevOps的完美邂逅:从Jenkins配置到自动化部署,全流程解析持续集成与持续交付的最佳实践
【8月更文挑战第31天】WPF与DevOps的结合开启了软件生命周期管理的新篇章。通过Jenkins等CI/CD工具,实现从代码提交到自动构建、测试及部署的全流程自动化。本文详细介绍了如何配置Jenkins来管理WPF项目的构建任务,确保每次代码提交都能触发自动化流程,提升开发效率和代码质量。这一方法不仅简化了开发流程,还加强了团队协作,是WPF开发者拥抱DevOps文化的理想指南。
39 1
|
22天前
|
Java 微服务 Spring
Spring Cloud全解析:配置中心之解决configserver单点问题
但是如果该configserver挂掉了,那就无法获取最新的配置了,微服务就出现了configserver的单点问题,那么如何避免configserver单点呢?
|
20天前
|
持续交付 jenkins C#
“WPF与DevOps深度融合:从Jenkins配置到自动化部署全流程解析,助你实现持续集成与持续交付的无缝衔接”
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)项目中应用DevOps实践,实现自动化部署与持续集成。通过具体代码示例和步骤指导,介绍选择Jenkins作为CI/CD工具,结合Git进行源码管理,配置构建任务、触发器、环境、构建步骤、测试及部署等环节,显著提升开发效率和代码质量。
37 0
|
22天前
|
机器学习/深度学习 计算机视觉 Python
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
31 0
|
25天前
|
XML Java 应用服务中间件
SpringBoot启动流程解析
SpringBoot启动流程解析
26 0
|
27天前
|
安全 数据安全/隐私保护
|
27天前
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
|
14天前
|
安全
CORS 跨域资源共享的实现原理
CORS 跨域资源共享的实现原理

热门文章

最新文章

推荐镜像

更多