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


相关文章
|
3月前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
124 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
3月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
169 0
|
3月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
47 0
|
3月前
|
Java 数据库连接 数据库
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——MyBatis 介绍和配置
本文介绍了Spring Boot集成MyBatis的方法,重点讲解基于注解的方式。首先简述MyBatis作为持久层框架的特点,接着说明集成时的依赖导入,包括`mybatis-spring-boot-starter`和MySQL连接器。随后详细展示了`properties.yml`配置文件的内容,涵盖数据库连接、驼峰命名规范及Mapper文件路径等关键设置,帮助开发者快速上手Spring Boot与MyBatis的整合开发。
178 0
|
3月前
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th=&quot;http://www.thymeleaf.org&quot;`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
85 0
|
3月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
92 0
|
3月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
在微服务架构中,随着业务复杂度增加,项目可能需要调用多个微服务。为避免使用`@Value`注解逐一引入配置的繁琐,可通过定义配置类(如`MicroServiceUrl`)并结合`@ConfigurationProperties`注解实现批量管理。此方法需在配置文件中设置微服务地址(如订单、用户、购物车服务),并通过`@Component`将配置类纳入Spring容器。最后,在Controller中通过`@Resource`注入配置类即可便捷使用,提升代码可维护性。
55 0
|
1月前
|
人工智能 前端开发 JavaScript
webpack-dev-server代理后端一直报CORS跨域或500错误
在Vue项目中使用Webpack的devServer代理后端接口时,遇到500错误。问题根源在于浏览器请求中携带的Origin头导致服务器报错,而Postman测试正常。通过分析发现,调整或移除Origin头可解决问题。解决办法包括:1) 在代理配置中添加正确的Origin头;2) 删除请求中的Origin头。文章还深入解析了Origin头的作用及changeOrigin配置的实际意义,并附带相关文档链接,帮助开发者更好地理解与解决类似跨域问题。
|
3月前
|
前端开发 JavaScript 应用服务中间件
前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy
跨域问题是前端开发中常见且棘手的问题,但通过理解CORS的工作原理并应用合适的解决方案,如服务器设置CORS头、使用JSONP、代理服务器、Nginx配置和浏览器插件,可以有效地解决这些问题。选择合适的方法可以确保应用的安全性和稳定性,并提升用户体验。
1481 90
|
3月前
|
JSON 缓存 前端开发
对CORS(跨域)的一些见解
CORS(跨域资源共享)是W3C标准,用于解决AJAX跨源请求限制。浏览器与服务器需共同支持CORS,浏览器自动处理请求头,开发者无需额外操作。CORS分为简单请求与非简单请求:简单请求满足特定条件(如方法为GET/POST/HEAD且头信息有限制),浏览器直接发送;非简单请求需先进行“预检”请求(OPTIONS方法),确认服务器允许后才发送实际请求。服务器回应需包含Access-Control-Allow-Origin等字段,以控制跨域访问权限。
106 10

推荐镜像

更多
  • DNS
  • 下一篇
    oss创建bucket