SpringBoot 自定义Filter 提前返回 CORS 错误 处理前后端分离跨域配置无效问题解析

前言

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

准备工作

https://gitee.com/youlaiorg/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 {@Beanpublic 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 方法

CORS 配置失效原理分析

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

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

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

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

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

在这里插入图片描述

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

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

在这里插入图片描述

CORS 配置失效解决方案

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

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

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

解决方案一 (推荐)

配置 CorsFilter 优先于 SpringSecurityFilter 执行

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


在这里插入图片描述

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

/*** CORS资源共享配置** @author haoxr* @date 2023/4/17*/
@Configuration
public class CorsConfig {@Beanpublic 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;}
}

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

在这里插入图片描述

解决方案二

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

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

@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http ...// 走 Spring Security 过滤器链的放行配置.requestMatchers(HttpMethod.OPTIONS,"/**").permitAll() // 放行预检请求.anyRequest().authenticated();return http.build();}@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {// 不走过滤器链的放行配置return (web) -> web.ignoring().requestMatchers(HttpMethod.OPTIONS,"/**") // 放行预检请求}

或者

		  //放行 options 的请求if ("OPTIONS".equals(request.getMethod())) {filterChain.doFilter(request, servletResponse);}

基础的跨域共享配置

@Configuration
public class CorsConfig {@Beanpublic 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)));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/424376.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Python自动化操作手机,自动执行常见任务,例如滑动手势、呼叫、发送短信等等

使用Python自动化操作手机,自动执行常见任务,例如滑动手势、呼叫、发送短信等等。 此自动化脚本将帮助你使用 Python 中的 Android 调试桥 (ADB) 自动化你的智能手机。下面我将展示如何自动执行常见任务,例如滑动手势、呼叫、发送短信等等。 您可以了解有关 ADB 的更多信息,…

采用企业应用开发平台实现提质增效!

当前&#xff0c;为了实现提质增效的办公目的&#xff0c;有不少企业都倾向于使用低代码技术平台&#xff0c;企业应用开发平台就是大家常用的软件平台&#xff0c;由于具有效率高、简便灵活、可视化设计等诸多优势特点&#xff0c;因而在业务量上涨的现代化职场办公中&#xf…

Postman工作协同:生成接口文档和示例,超实用

Postman这个Documentation功能对于前后端分离的团队&#xff0c;接口开发团队来说真的是提升沟通效率和工作效率的一个利器。废话不多说&#xff0c;直接上干货来看如何通过Postman来生成接口文档和发布接口文档&#xff0c;以及如何定制化文档中的内容。 流程概要&#xff1a…

牛仔服装行业研究:预计到2025年将达到约650亿美元

牛仔服装是指原美国西部垦拓者(牛仔)穿着的服装&#xff0c;一般用纯棉或棉纤维为主要原材料混纺、交织的色织牛仔布制作。 牛仔布的生产起源于美国。牛仔服装历经百年变迁&#xff0c;由最初的工装演变为时装&#xff0c;征服了人们不同阶段挑剔的目光&#xff0c;最终成为服装…

伊恩·斯图尔特《改变世界的17个方程》麦克斯韦方程方程笔记

它告诉我们什么&#xff1f; 电和磁并不会随便乱跑。旋转的电场区域会产生垂直于旋转方向的磁场。旋转的磁场区域也会产生垂直于旋转方向的电场&#xff0c;但方向相反。 为什么重要&#xff1f; 这是物理力的第一次重大统一&#xff0c;表明电和磁是密切相关的。 它带来了什么…

echarts绘制多条刻度线仪表盘,文本内容带背景且颜色渐变,echarts绘制复杂仪表盘

option {series: [{// 最外圈type: gauge,radius: 80%,center: [50%, 90%],startAngle: 180,endAngle: 0,min: 0,max: 100,progress: {show: false,width: 8},pointer: {show: false},axisLine: {show: false,lineStyle: {width: 50,color: [// axisTick使用的是这里的颜色[0.…

Java项目:基于ssm框架实现的电影评论系统(ssm+B/S架构+源码+数据库+毕业论文)

一、项目简介 本项目是一套ssm826基于ssm框架实现的电影评论系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#x…

切鱼达人,Android休闲游戏开发

使用 Android Studio 开发了一款休闲游戏 —— 《切鱼达人》 A. 项目描述 《切鱼达人》是一款有趣的休闲游戏app&#xff0c;让玩家在虚拟的海洋世界中体验切割各种鱼类的乐趣。 《切鱼达人》玩法简单而富有挑战性。玩家需要在游戏中扮演一名渔民&#xff0c;使用手指在屏幕上…

测试不拘一格——掌握Pytest插件pytest-random-order

在测试领域,测试用例的执行顺序往往是一个重要的考虑因素。Pytest插件 pytest-random-order 提供了一种有趣且灵活的方式,让你的测试用例能够以随机顺序执行。本文将深入介绍 pytest-random-order 插件的基本用法和实际案例,助你摆脱固定的测试顺序,让测试更具变化和全面性…

【动态规划】879. 盈利计划

作者推荐 【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径 本文涉及知识点 动态规划汇总 LeetCode879. 盈利计划 集团里有 n 名员工&#xff0c;他们可以完成各种各样的工作创造利润。 第 i 种工作会产生 profit[i] 的利润&#xff0c;它要求 group[…

V-bind缩写、V-on缩写、V-if、V-show、V-for、Computed计算属性、methods属性、监听属性watch实例

V-bind、V-for缩写 V-model V-if V-show V-for Computed计算属性 声明了一个计算属性 reversedMessage 。 提供的函数将用作属性 vm.reversedMessage 的 getter 。 vm.reversedMessage 依赖于 vm.message&#xff0c;在 vm.message 发生改变时&#xff0c;vm.reversedMessage…

性能优化(CPU优化技术)-NEON指令介绍

「发表于知乎专栏《移动端算法优化》」 本文主要介绍了 NEON 指令相关的知识&#xff0c;首先通过讲解 arm 指令集的分类&#xff0c;NEON寄存器的类型&#xff0c;树立基本概念。然后进一步梳理了 NEON 汇编以及 intrinsics 指令的格式。最后结合指令的分类&#xff0c;使用例…