Spring Boot CORS跨域配置与排错:前后端分离联调指南

发布时间:2026/9/19 15:04:20

Spring Boot CORS跨域配置与排错:前后端分离联调指南 简介Spring Boot 开发者常遇到的跨域问题在这份 PDF 文档中得到系统梳理资源面向 Java Web 开发者和前后端分离项目维护人员讲解 CORS 跨域资源共享机制及其在 Spring Boot 中的落地。文档按两条主线展开一是自定义 CorsFilter重写 OncePerRequestFilter 的 doFilterInternal 方法在响应头中配置 Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Allow-Methods、Access-Control-Max-Age 和 Access-Control-Allow-Headers并处理 OPTIONS 预检请求二是通过 Configuration 定义 CorsConfig结合 UrlBasedCorsConfigurationSource、CorsConfiguration 与 FilterRegistrationBean 完成全局配置可通过 addAllowedOrigin、addAllowedHeader、addAllowedMethod 精确控制允许的域名、请求头和请求方法同时用 setOrder 控制过滤器优先级。PDF 内包含可直接参考的 Filter 实现和配置类代码片段也结合跨域概念解释配置背后的原理并点明两种方式各自的适用场景。包体为 1 个 PDF 文件压缩包约 34KB轻量易读目前已有 2834 人浏览学习。文档还总结了两种方式的优缺点和选型建议适合需要快速排查前后端联调跨域报错、或想在不同项目中灵活选择配置方式的 Java 工程师。1. springboot cors 跨域报错是前后端分离最常见的联调问题springboot cors 跨域报错是前后端分离项目最常见的联调问题页面在 5173 端口接口在 8080 端口前端一个 fetch 就抛 has been blocked by cors policy: no access-control-allow-origin header is present。这个报错和 Spring Boot 本身没关系请求实际到了后端是浏览器读到响应没有 Access-Control-Allow-Origin 头才拒绝放行。springboot 配 cors 的实质是让 MVC 按规则给响应补头、按规范处理 OPTIONS 预检最常见的就是全局配置和 CrossOrigin 注解两种方式。文章按浏览器检查顺序讲参数、allowedOriginPatterns 与 credentials 的坑以及拦截器和 Security 如何破坏配置适合联调被跨域卡住的人也够得着 springboot 面试里 cors 配置错误这道题。2. 预检机制与 6 个响应头配 springboot cors 前先看清浏览器要什么2.1 简单请求与预检请求的分界浏览器不是对所有跨域请求都先发 OPTIONS。满足全部条件的叫简单请求方法落在 GET/HEAD/POSTContent-Type 只能是 application/x-www-form-urlencoded、multipart/form-data、text/plain且没有 authorization 之类的自定义头。满足时浏览器直接发真实请求后端只要在响应里带 Access-Control-Allow-Origin 就能通过。条件任一不满足——接口要求 application/json、调用方带了 Authorization 头、用了 PUT/DELETE——浏览器就先发 OPTIONS 预检请求头里带 Access-Control-Request-Method 和 Access-Control-Request-Headers问我打算这么调你让不让。后端匹配并返回一组 Access-Control-Allow-* 头预检算通过不匹配或没配 cors浏览器直接拦掉真实请求console 里就是那段 blocked 文案。所以排查第一步永远是打开 Network 看有没有 OPTIONS 记录有是预检环节挂了没有是真实响应缺头。2.2 Access-Control-* 响应头参数与手动预检命令六个响应头决定预检和真实请求是否通过它们在 Spring 里的配置入口如下表响应头配置入口作用翻车点Access-Control-Allow-OriginallowedOrigins / allowedOriginPatterns声明允许哪个来源读响应配了*又开 credentialsAccess-Control-Allow-MethodsallowedMethods预检放行的 HTTP 方法漏掉 PUT/DELETEAccess-Control-Allow-HeadersallowedHeaders预检放行的自定义请求头漏掉 authorizationAccess-Control-Allow-CredentialsallowCredentials是否允许带 cookie 凭据与*冲突Access-Control-Expose-HeadersexposedHeaders前端 JS 能读到的响应头白名单漏掉 X-Total-CountAccess-Control-Max-AgemaxAge预检结果在浏览器缓存秒数设太大导致改配置不生效# 在本地复现浏览器预检路径换成实际接口 curl -i -X OPTIONS http://localhost:8080/api/v1/order/1 \ -H Origin: http://localhost:5173 \ -H Access-Control-Request-Method: GET \ -H Access-Control-Request-Headers: authorization命令说明-i打印响应头三组-H是浏览器发预检时原样带上的内容后端返回的 Access-Control-Allow-Origin 等于http://localhost:5173说明 MVC 的 cors 开关已开。参数说明Origin 必须和页面实际域名端口完全一致带不带 authorization 那句决定 Access-Control-Request-Headers 是否出现业务里用了 token 就一定会有。2.3 Spring 家族里 cors 生效的两层位置第一层在 DispatcherServlet 内部由 AbstractHandlerMapping 负责。它按路径找到 HandlerExecutionChain 后把合并好的 CorsConfiguration 包装成 CorsInterceptor 塞进执行链预检请求在这一步由 DefaultCorsProcessor 直接写响应不会进入 Controller。addCorsMappings 写的全局配置和 CrossOrigin 注解都注入到这一层。第二层是 CorsFilter一个普通 Servlet Filter跑在 DispatcherServlet 之前不依赖路径匹配Spring Security 和从旧 springmvc 工程改造过来的 Filter 链里都能用。Spring Boot 自动配置原理里有对应的 CorsAutoConfiguration项目没定义 CorsFilter Bean、但写了 spring.web.cors.* 属性时它会自动装配一个 CorsFilter。大多数人不会走属性文件这条路因为写不了复杂规则但对springmvc 工程如何改造成 springboot 工程的老项目这个自动装配常常是重复响应头的来源。提示同一个请求可能被两层同时处理。Filter 补一次头HandlerMapping 再补一次头浏览器看到重复的 Access-Control-Allow-Origin 直接拒绝这是配完还报 blocked 的隐藏原因。3. 方式一addCorsMappings 全局配置 springboot cors 与 allowedOriginPatterns3.1 最小全局配置与链式参数表Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:5173, https://admin.example.com) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .exposedHeaders(X-Token, Content-Disposition) .allowCredentials(true) .maxAge(3600); } }代码逻辑说明实现 WebMvcConfigurer 的 Configuration 会在启动时被回调addCorsMappings 把 CorsRegistration 注册到 RequestMappingHandlerMapping 的全局配置里所有匹配路径的接口共用一份规则。addMapping 是路径匹配/api/**只覆盖业务接口/**最省事但后面挂 Security 时范围越宽越难管。链式参数对应行为默认值说明allowedOriginsAccess-Control-Allow-Origin无白名单 Origin与 allowCredentials(true) 并存时禁止*allowedOriginPatternsAccess-Control-Allow-Origin无通配模式匹配后回显请求方 OriginallowedMethodsAccess-Control-Allow-MethodsGET/HEAD/POST漏配 PUT/DELETE 时预检直接失败allowedHeadersAccess-Control-Allow-Headers默认不限制显式配置后才按白名单校验exposedHeadersAccess-Control-Expose-Headers空不配的话前端 getResponseHeader 拿不到值allowCredentialsAccess-Control-Allow-Credentialsfalsetrue 表示允许携带 cookie 与 AuthorizationmaxAgeAccess-Control-Max-Age1800 秒浏览器缓存预检结果的时间allowedMethods 默认只有 GET/HEAD/POST 是新手最容易踩的点接口用 PUT 更新前端请求是发出去了但预检先失败Network 里始终只有一条 OPTIONS控制台却不解释为什么。3.2 allowedOrigins 与 allowedOriginPatternscredentialstrue 时的坑前端 axios 一旦withCredentials: true或后端接口要读 cookieAccess-Control-Allow-Origin 就不能是*必须是具体 Origin。老写法.allowedOrigins(*) .allowCredentials(true)在 Spring Framework 5.3 之前能启动但浏览器按规范拒绝5.3 引入 allowedOriginPatterns 后推荐替代方案到 Spring Boot 3 这一代直接这样写会在启动阶段抛 IllegalArgumentException报错信息就是这个场景的经典文案cors 配置错误(反射 origin credentialstrue)。这也是项目从 Boot 2.3 升到 Boot 3 后突然启动失败的高频原因跟 springboot 版本太高、行为收紧直接相关。正确做法是窄化来源或者用模式匹配registry.addMapping(/**) .allowedOriginPatterns(http://localhost:*, https://*.example.com) .allowedMethods(*) .allowCredentials(true);参数说明allowedOriginPatterns 不是把*原样塞进响应头而是后端拿请求 Origin 与模式比对匹配后把实际 Origin 回显到 Access-Control-Allow-Origin。localhost:*能覆盖前端经常变的随机端口*.example.com覆盖多级子域。安全审计严格的话还是建议显式 allowedOrigins 白名单模式匹配是便利和安全的折中。3.3 全局方案的另一个形态注册 CorsFilter Bean部分场景不适合走 MVC 层接口路径不归 RequestMapping 管、想在 Filter 链最前面处理、或者要跟 Spring Security 共用同一个配置源。常见做法是直接注册 CorsFilterBean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.setAllowedOriginPatterns(List.of(http://localhost:*)); config.setAllowedMethods(List.of(*)); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); }逻辑说明CorsFilter 构造器依赖一个 CorsConfigurationSourceUrlBasedCorsConfigurationSource 负责按路径返回配置registerCorsConfiguration 可以写多行做按模块的差异化放行。它与 addCorsMappings 的区别在于执行层级一个在 Servlet Filter一个在 MVC HandlerMapping两者同时存在就会产生重复头。我的建议是纯 Spring MVC 项目用 addCorsMappings涉及 Spring Security 时把配置抽成 CorsConfigurationSource 的 Bean 给两边共用避免维护两套。4. 方式二用 CrossOrigin 注解细粒度放行 springboot 接口4.1 类级与方法级的 CrossOrigin 写法RestController RequestMapping(/api/open) CrossOrigin(origins http://localhost:5173, maxAge 3600) public class OpenApiController { GetMapping(/health) public String health() { return ok; } GetMapping(/orders) CrossOrigin( origins {https://admin.example.com, https://ops.example.com}, allowedHeaders {authorization, content-type}, exposedHeaders X-Total-Count, allowCredentials true ) public ListOrder list() { return orderService.list(); } }代码逻辑说明类级注解对 Controller 下所有方法生效方法级注解与类级注解做合并同名属性以方法为准方法没写的属性沿用类级值。所以上面的 health 接口只放行 localhost:5173orders 接口额外放开两个线上来源且允许前端读取 X-Total-Count 响应头。CrossOrigin 的完整属性表属性类型默认值映射目标origins别名 valueString[]{}Access-Control-Allow-OriginallowedHeadersString[]{}Access-Control-Allow-HeadersmethodsRequestMethod[]{}为空时取 Controller 映射方法本身exposedHeadersString[]{}Access-Control-Expose-HeadersallowCredentialsString注意是字符串true/false不是布尔值maxAgelong-1预检缓存秒数-1 表示不产生该头新手最容易写错的是 allowCredentials 传了布尔 trueCrossOrigin 的属性类型是 String传 boolean 会直接编译报错。4.2 细粒度场景第三方来源、自定义请求头与暴露头CrossOrigin 适合系统里只有几个接口对外开放的局面比如健康检查、支付回调、开放平台 API。第三方来源往往不是一个域origins 数组直接列多个。前端要读 X-Total-Count 做分页后端必须 exposedHeaders 声明否则 getResponseHeader 返回 null前端请求头带了自定义 headerallowedHeaders 得包含它否则预检阶段就被否决。注解方式的优点是配置跟着接口走一个接口一套规则代码评审时看 Controller 就清楚谁对谁开放了跨域不需要再全局翻配置类。4.3 注解方案的边界这些场景 CrossOrigin 不生效注解只在请求到达 Spring MVC 的 HandlerMapping 之后才有意义。请求在 DispatcherServlet 之前被 Spring Security、网关或前置 Filter 拦截时注解配置根本没机会作用判断方法是看 Network 里失败响应有没有 Access-Control-Allow-Origin 头有说明 MVC 层处理了没有就往上找拦截层。路径没匹配到 Controller 时同理404 响应不会携带注解生成的跨域头前端报错前先确认 URL 前缀没拼错。另一个容易混的是权限语义CrossOrigin 只决定浏览器是否放行响应不代表接口匿名可访问。认证逻辑照常执行加了注解不等于免登录。实际项目里加了 CrossOrigin 还是 401大多是这个问题和后端 cors 配置本身没关系。5. springboot cors 配完还报 blocked 的 4 个排查点与 curl 验证5.1 排查点一Spring Security 把 OPTIONS 预检挡在门外Configuration public class SecurityConfig { Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(Customizer.withDefaults()) .authorizeHttpRequests(auth - auth .requestMatchers(HttpMethod.OPTIONS, /**).permitAll() .anyRequest().authenticated()); return http.build(); } }代码说明http.cors()会从容器里找 CorsConfigurationSource Bean没有就用默认实现OPTIONS 先放行否则预检请求会被认证逻辑拦截返回 401响应里自然没有跨域头。Spring Boot 2 的写法是antMatchers(HttpMethod.OPTIONS, /**).permitAll()原理相同。同时确认你在 Security 里用了同一个 CorsConfigurationSource别让 Security 读一套、MVC 用另一套。5.2 排查点二404、自定义拦截器与重复头preflight OPTIONS 请求如果路径不匹配任何 handlerHandlerMapping 直接返回 404响应头里不会有 Access-Control-Allow-Origin浏览器判定预检失败——这跟配置写没写对无关。前端 baseURL 多拼一段、后端 context-path 不一致都会触发。自定义拦截器是第二个坑很多 springboot 拦截器实现里校验登录态对没有 token 的 OPTIONS 直接 return false预检要求 2xx 响应401/403 照样被浏览器拦截。常见做法是在 preHandle 第一行放行 OPTIONS 请求再把业务校验放在后面。第三个常见失败是重复的 Access-Control-Allow-Origin 头。注解和 addCorsMappings 同时生效会叠加nginx、网关层手动 add_header 之后后端 Filter 又补一次也会出现两行同名头浏览器的报错文案是multiple values。用 curl 看响应头两行一样的值就是证据。5.3 用 curl 和 Vary 头验证 cors 配置是否真正生效# 验证真实请求的响应头 curl -s -D - -o /dev/null http://localhost:8080/api/v1/order/1 \ -H Origin: http://localhost:5173 # 验证预检请求前端最常见的失败环节 curl -s -D - -o /dev/null -X OPTIONS http://localhost:8080/api/v1/order/1 \ -H Origin: http://localhost:5173 \ -H Access-Control-Request-Method: GET \ -H Access-Control-Request-Headers: authorization命令说明-D -把响应头打印到标准输出-o /dev/null丢弃 body。看完输出只判断三件事Access-Control-Allow-Origin 的值是否和请求 Origin 完全一致含端口Vary头里是否带 Origin这是缓存区分来源的关键标志credentials 场景下 Allow-Origin 不允许是*。三条都过配置本身没问题剩下的怀疑对象就是浏览器缓存和上层代理任意一条不过直接用返回的状态码和缺失头去定位是哪一层没放行。浏览器里改完配置还报旧错时开发期把 maxAge 临时设成 0让每次请求都重新预检联调通过后再提到 600 秒收尾——用这招能过滤掉一半我明明改对了的自我怀疑。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/19 14:59:20

dotnet演进史:从Framework到.NET 8的兼容与选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 14:59:20

人工智能驱动的税务审计异常检测:从规则引擎到机器学习

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 14:59:20

接收机灵敏度全解析:从热噪声到工程优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 15:59:22

Flutter 3.35 Impeller花屏排查实录:从线上事故到渲染适配

1. 从一次线上事故说起:Impeller 在 3.35 上翻车了那天下午刚发完版,测试同学在群里甩了一张截图,画面上一片横向撕裂的彩色条纹,像老式电视机信号丢失那种花屏。第一反应是"是不是某个页面用了自定义 Shader"&#xff…

2026/9/19 15:59:22

信创环境下WordPress粘贴图片转存失败排查与修复实战

前阵子我在一个信创系统环境里处理WordPress粘贴图片转存问题,后台从剪贴板贴一张截图,转存经常失败。普通电脑上好好的操作,一到国产操作系统加国产浏览器的组合就各种报错:有的粘贴后显示“HTTP错误”,有的图片在编辑…

2026/9/19 15:59:22

两层CNN在COREL1000上达93%准确率的轻量图像分类实现

简介:本资源是一份面向深度学习初学者与图像处理从业者的专业指导型技术文档,聚焦卷积神经网络(CNN)在图像分类任务中的原理、结构设计与实证对比。文档系统解析CNN的输入层、双卷积层、双池化层、全连接层及Softmax输出层构成&am…

2026/9/19 15:59:22

基于DeepSeek联邦学习的零售多门店销量预测与库存优化

简介:围绕零售库存管理中的多门店销量预测难题,这套PDF文档系统讲解如何借助DeepSeek与联邦学习构建预测系统。文档面向零售数据分析师、供应链管理人员以及正在学习人工智能应用的开发者,旨在帮助读者解决数据分散、隐私保护难和预测准确性不…

2026/9/19 15:59:22

AI赋能数字水务:从数据治理到调度优化的关键技术与实践

简介:人工智能赋能数字水务白皮书以PDF格式呈现,完整聚焦数字水务的智能化升级路径,适合水务集团技术人员、智慧城市规划者及环境保护领域的研究者阅读。白皮书围绕人工智能与水务场景的融合,详细阐述了机器学习预测水质污染风险、…

2026/9/19 15:54:22

Excel练习素材大全:从基础操作到数据透视表实战

刚开始带教新人的时候,我经常被问到同一个问题:“老师,有没有现成的Excel练习数据?我自己随便敲几行数字,练起来总觉得不得劲。”这还真不是矫情,Excel这东西,光看视频教程、光背函数语法&#…

2026/9/18 14:13:01

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/19 0:03:10

验证 OpenSpec 兼容性,Cursor 的 Token 从 TaoToken 出

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 0:03:10

书桌角落的 Mac mini,OpenClaw 通过 TaoToken 跑任务。

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 0:03:10

oh-my-hermes:打造跨工具的命令编排与插件化工作流

1. 项目概述与设计初衷1.1 它到底是什么先说结论:oh-my-hermes 是一个面向开发者日常终端操作的效率工具套件,核心定位是“把分散在各类命令行工具里的高频操作,统一收拢成一套插件化、可编排的工作流”。项目灵感来源很明显——oh-my-zsh 重…

2026/9/18 14:13:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/18 14:13:02

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/18 14:13:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码