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

发布时间:2026/11/6 2:48:51

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/10/25 12:29:57

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/10/26 3:14:57

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

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

2026/11/5 0:01:29

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

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

2026/11/6 2:44:01

解决VSCode远程SSH连接中CUDA不可用问题

1. 问题现象与背景分析最近在配置远程开发环境时遇到一个典型问题:通过VSCode的SSH远程连接服务器后,虽然能正常启动Jupyter Notebook,但GPU加速的CUDA计算却无法工作。这种场景在深度学习开发中非常常见——我们通常用本地VSCode编写代码&am…

2026/11/6 2:44:01

OpenCode开源AI编程助手:多模型支持与实战应用

1. OpenCode:开源AI编程助手的崛起与实战指南在AI编程工具领域,Claude Code长期占据主导地位,但其封闭的生态系统和有限的模型支持让许多开发者感到束缚。OpenCode的出现打破了这一局面——这个开源项目在GitHub上发布仅数月就斩获51.7k星标&…

2026/11/6 2:44:01

酒店智能话务台系统架构与优化实践

1. 酒店话务台升级的行业痛点与机遇在酒店行业干了十几年,我见过太多前台手忙脚乱接电话的场景。记得有次凌晨两点,亲眼目睹值班同事同时接听客房服务、处理投诉电话、记录叫醒服务,最后把客人房号都记混了。这种传统话务模式存在三个致命伤&…

2026/11/6 2:44:01

jy901磁加双模校准:嵌入式磁场椭球拟合与加速度零偏标定实战

简介:本资源是一份面向嵌入式开发者与惯性导航初学者的MPU9050传感器校准技术文档,聚焦JY901模块的磁场与加速度双通道硬件级校准实践。针对磁力计易受硬铁/软铁干扰、加速度计存在零偏漂移等典型问题,文档系统梳理了平面校准与三维校准两种磁…

2026/11/6 2:44:01

GIS折线顶点打断:原理、实现与批量处理实践

做矢量数据处理的人,多少都碰过这种场景:一条几百公里长的管线、一条绕了半座城的道路、或者一份从外部拿到的河道中心线,领导和下游系统只提了一个要求——在某几个节点处把它断开,变成一段一段的独立要素。听起来是个五分钟的活…

2026/11/6 2:39:01

原生应用选型、架构与性能优化实战指南

原生应用这四个字,放在五年前是个默认选项,放在今天反而成了一个需要反复论证的选择。我最近刚把一个团队从跨平台方案迁回原生,整个过程踩的坑比预想的多得多,也让我重新梳理了一遍:什么样的项目真的该做原生&#xf…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

2026/11/6 1:03:39

MCU晶振电路设计全解析:从负载电容计算到PCB布局调试

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

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

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

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

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