Spring Boot 結合 CORS 解決前端跨域問題
1. 背景
在前后端分離的項目中,前端(例如 http://localhost:3000
)調用后端接口(例如 http://localhost:8080
)時,瀏覽器會因為 同源策略 限制而阻止請求,這就是所謂的 跨域問題。
同源策略要求:
- 協議(Protocol)
- 域名(Domain)
- 端口(Port)
三者必須完全一致,否則就會觸發跨域。
跨域在前端調試、微服務接口調用、第三方 API 請求等場景中非常常見。為了讓瀏覽器允許跨域訪問,需要配置 CORS(跨域資源共享,Cross-Origin Resource Sharing)。
2. 什么是 CORS
CORS 是 W3C 定義的一種跨域訪問標準,允許服務器在響應中添加特定的 HTTP 頭,讓瀏覽器判斷是否允許跨域請求。核心是以下響應頭:
響應頭 | 作用 |
---|---|
Access-Control-Allow-Origin | 允許訪問的源(可以是具體域名或 * ) |
Access-Control-Allow-Methods | 允許的 HTTP 方法(GET, POST, PUT, DELETE 等) |
Access-Control-Allow-Headers | 允許的自定義請求頭 |
Access-Control-Allow-Credentials | 是否允許攜帶 Cookie |
Access-Control-Max-Age | 預檢請求緩存時間(秒) |
3. Spring Boot 中解決跨域的常用方法
3.1 方法一:在 Controller 上使用 @CrossOrigin
Spring Boot 提供了 @CrossOrigin
注解,可以快速在類或方法級別開啟 CORS。
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/user")
@CrossOrigin(origins = "http://localhost:3000") // 允許的前端地址
public class UserController {@GetMapping("/{id}")public String getUser(@PathVariable Long id) {return "用戶ID: " + id;}
}
特點:
- 適合局部跨域控制。
- 不影響其他接口。
- 缺點是需要在每個 Controller 手動加注解,管理不便。
3.2 方法二:全局 CORS 配置(推薦)
如果你的接口都需要跨域訪問,可以通過 WebMvcConfigurer
全局配置。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class GlobalCorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 允許跨域的接口路徑.allowedOriginPatterns("*") // 允許跨域的源(Spring Boot 2.4+ 推薦用 allowedOriginPatterns).allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允許的 HTTP 方法.allowedHeaders("*") // 允許的請求頭.allowCredentials(true) // 是否允許攜帶 Cookie.maxAge(3600); // 預檢請求緩存時間}
}
特點:
- 一次配置,所有接口生效。
- 方便統一管理。
- 生產環境建議改成精確匹配允許的域名,而不是
*
。
3.3 方法三:使用 Filter 配置 CORS
通過自定義 CorsFilter
處理跨域請求,可以在 Spring Boot 啟動時加載。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;@Configuration
public class CorsFilterConfig {@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.addAllowedOriginPattern("*");config.setAllowCredentials(true);config.addAllowedMethod("*");config.addAllowedHeader("*");config.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}
特點:
- 適用于需要跨越 Spring MVC、Spring Security 等多個層的情況。
- 能解決某些全局配置不生效的問題。
4. 配合 Spring Security 的特殊處理
如果項目使用了 Spring Security,默認會攔截 OPTIONS 預檢請求,導致 CORS 配置不生效。這時需要額外在 Security 配置中開放 OPTIONS 請求。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;@Configuration
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.cors().and().csrf().disable(); // 啟用 CORS & 禁用 CSRFhttp.authorizeHttpRequests().anyRequest().permitAll();return http.build();}
}
關鍵點:
http.cors()
會讀取WebMvcConfigurer
或CorsFilter
中的配置。- 關閉 CSRF 主要是為了方便調試跨域(生產環境需根據業務需求啟用)。
5. 常見問題排查
- 前端依舊報跨域
- 確認是瀏覽器報的 CORS 錯誤,而不是接口 404/500。
- 確認后端響應中包含
Access-Control-Allow-Origin
。
- Cookie 不生效
- 后端
allowCredentials(true)
。 - 前端請求設置
fetch
或 Axios 的withCredentials: true
。 - 注意:當
allowCredentials(true)
時,Access-Control-Allow-Origin
不能為*
。
- 后端
- Spring Security 版本沖突
- Spring Boot 3.x 下要用
SecurityFilterChain
而不是WebSecurityConfigurerAdapter
(已廢棄)。
- Spring Boot 3.x 下要用
6. 總結
- 局部跨域 →
@CrossOrigin
- 全局跨域(推薦) →
WebMvcConfigurer
- 復雜場景(如結合 Spring Security) →
CorsFilter
+SecurityConfig
- 生產環境建議精確配置
allowedOrigins
,避免安全隱患。
? 最佳實踐推薦
@Configuration
public class GlobalCorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("https://yourdomain.com") // 精確指定.allowedMethods("GET", "POST", "PUT", "DELETE").allowedHeaders("*").allowCredentials(true).maxAge(3600);}
}
配合:
@Configuration
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.cors().and().csrf().disable();return http.build();}
}
這樣前端就可以愉快地調用后端接口而不會再被 CORS 卡住了。
7.本質
CORS 在 Spring Boot 里的本質,其實并不是靠普通的 攔截器(HandlerInterceptor) 去做的,而是由 底層的 CORS 處理機制 在 Servlet Filter 層完成的。
7.1. 核心機制
在 Spring Web(Spring MVC)里,CORS 處理是通過 CorsProcessor
在請求進入 Controller 之前攔截,并根據配置動態添加 CORS 響應頭。
整個流程分兩種情況:
- 預檢請求(OPTIONS)
- 瀏覽器先發一個 OPTIONS 請求,詢問服務器是否允許該跨域。
- Spring 的 CORS 處理器判斷后直接返回帶有
Access-Control-Allow-*
的響應,并終止后續調用,不進入 Controller。
- 實際請求(GET、POST…)
- 在進入 Controller 前,CORS 處理器檢查請求來源和方法是否合法,如果允許,就在響應頭里加上 CORS 相關字段。
7.2. Spring Boot 的執行鏈
當你用
@CrossOrigin
WebMvcConfigurer#addCorsMappings()
- 或
CorsFilter
配置 CORS 后,Spring Boot 會:
- 注冊一個
CorsFilter
或HandlerMappingIntrospector
內部的 CORS 攔截邏輯。 - 請求進入 Servlet 容器后,先走 Filter 鏈,Spring CORS 邏輯會最先判斷跨域。
- 如果是 OPTIONS 預檢且允許跨域,直接返回響應,不再走后續 Controller。
- 如果是實際請求,進入 Controller 處理業務邏輯,但響應會自動加上 CORS 頭。
7.3. 為什么不用普通攔截器
- 普通的
HandlerInterceptor
只能攔截已經匹配到 Handler 的請求,但 CORS 預檢請求往往不需要進入業務 Controller。 - CORS 要盡早處理,最好在 Filter 層攔截,這樣性能更好、邏輯更統一。
- Spring MVC 的 CORS 是在 DispatcherServlet 處理請求前就能處理的,而攔截器是在 HandlerAdapter 調用前才運行。
? 總結一句話:
Spring Boot 的 CORS 本質是通過 Servlet Filter 層的 CorsFilter
+ Spring MVC 的 CorsProcessor
在請求到達 Controller 前處理的,而不是靠普通的業務攔截器實現的。