SpringBoot中處理跨域請求CORS的全面指南
一、CORS基礎(chǔ)概念
1.1 什么是跨域請求
跨域資源共享(Cross-Origin Resource Sharing, CORS)是一種安全機制,它允許Web應(yīng)用程序在一個域上的資源請求另一個域上的資源。瀏覽器出于安全考慮,會阻止不同源之間的AJAX請求,這是**同源策略(Same-Origin Policy)**的限制。
1.2 同源策略定義
兩個URL被認為是同源的條件:
- 協(xié)議相同(http/https)
- 域名相同
- 端口相同
例如:
- http://example.com/app1 和 http://example.com/app2 → 同源
- http://example.com 和 https://example.com → 不同源(協(xié)議不同)
- http://example.com 和 http://api.example.com → 不同源(域名不同)
- http://example.com 和 http://example.com:8080 → 不同源(端口不同)
二、Spring Boot處理CORS的5種方式
2.1 全局配置(推薦)
方式1:使用WebMvcConfigurer接口
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 所有接口 .allowedOrigins("*") // 允許所有源 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允許方法 .allowedHeaders("*") // 允許所有頭 .allowCredentials(true) // 允許憑證 .maxAge(3600); // 預(yù)檢請求緩存時間 } }
方式2:使用Filter方式(適用于Servlet應(yīng)用)
@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }
2.2 控制器方法級配置
方式3:使用@CrossOrigin注解
@RestController @RequestMapping("/api") public class UserController { // 單個方法配置 @CrossOrigin(origins = "http://localhost:3000") @GetMapping("/users") public List<User> getUsers() { // ... } // 整個控制器配置 @CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping("/products") public class ProductController { // ... } }
2.3 屬性文件配置(Spring Boot 2.4+)
方式4:application.yml配置
spring: mvc: cors: allowed-origins: "http://localhost:3000, https://example.com" allowed-methods: "GET, POST, PUT, DELETE" allowed-headers: "*" exposed-headers: "Authorization, Content-Disposition" allow-credentials: true max-age: 1800
等效的application.properties:
spring.mvc.cors.allowed-origins=http://localhost:3000, https://example.com spring.mvc.cors.allowed-methods=GET, POST, PUT, DELETE spring.mvc.cors.allowed-headers=* spring.mvc.cors.exposed-headers=Authorization, Content-Disposition spring.mvc.cors.allow-credentials=true spring.mvc.cors.max-age=1800
2.4 響應(yīng)頭手動設(shè)置
方式5:手動添加響應(yīng)頭(靈活但繁瑣)
@RestController public class ApiController { @GetMapping("/manual") public ResponseEntity<String> manualCors() { return ResponseEntity.ok() .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Methods", "GET") .body("Manual CORS configured"); } }
三、CORS處理深度解析
3.1 預(yù)檢請求(Preflight Request)
對于"非簡單請求",瀏覽器會先發(fā)送OPTIONS預(yù)檢請求:
簡單請求條件:
1.使用GET、HEAD或POST方法
2.僅包含以下頭:
- Accept
- Accept-Language
- Content-Language
- Content-Type (僅限 application/x-www-form-urlencoded, multipart/form-data, text/plain)
非簡單請求示例:
fetch('http://api.example.com/data', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-Custom-Header': 'value' }, body: JSON.stringify({key: 'value'}) });
Spring Boot會自動處理OPTIONS請求,無需開發(fā)者額外編碼。
3.2 核心響應(yīng)頭說明
響應(yīng)頭 | 說明 |
---|---|
Access-Control-Allow-Origin | 允許訪問的源,* 表示任何源 |
Access-Control-Allow-Methods | 允許的HTTP方法 |
Access-Control-Allow-Headers | 允許的請求頭 |
Access-Control-Expose-Headers | 允許瀏覽器訪問的響應(yīng)頭 |
Access-Control-Allow-Credentials | 是否允許發(fā)送Cookie和HTTP認證信息 |
Access-Control-Max-Age | 預(yù)檢請求結(jié)果的緩存時間(秒) |
3.3 常見問題解決方案
問題1:allowCredentials(true)與allowedOrigins("*")沖突
錯誤:
When allowCredentials is true, allowedOrigins cannot contain the special value "*"
解決方案:
// 替換為具體域名 .allowedOrigins("http://localhost:3000", "https://example.com")
問題2:前端仍然報CORS錯誤
檢查步驟:
- 確保后端已正確配置
- 檢查瀏覽器控制臺錯誤詳情
- 使用Postman等工具驗證接口是否正常工作
- 檢查是否有多個CORS配置相互覆蓋
問題3:自定義過濾器干擾CORS
解決方案:
確保CorsFilter在過濾器鏈中的優(yōu)先級:
@Bean public FilterRegistrationBean<CorsFilter> corsFilterRegistration() { FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(corsFilter()); registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高優(yōu)先級 return registration; }
四、安全最佳實踐
4.1 生產(chǎn)環(huán)境配置建議
@Configuration public class ProdCorsConfig implements WebMvcConfigurer { @Value("${app.cors.allowed-origins}") private String[] allowedOrigins; @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins(allowedOrigins) .allowedMethods("GET", "POST") .allowedHeaders("Content-Type", "Authorization") .exposeHeaders("X-Custom-Header") .allowCredentials(true) .maxAge(3600); } }
4.2 結(jié)合Spring Security
當(dāng)使用Spring Security時,需要確保CORS配置在安全過濾器之前:
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and() // 啟用Spring Security的CORS支持 .csrf().disable() .authorizeRequests() // 其他配置... } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("https://trusted.com")); configuration.setAllowedMethods(Arrays.asList("GET", "POST")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
4.3 監(jiān)控與日志
添加CORS請求日志:
@Bean public FilterRegistrationBean<CorsFilter> corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // ...配置 FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new CorsFilter(source) { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { log.info("CORS請求: {} {}", request.getMethod(), request.getRequestURI()); super.doFilterInternal(request, response, filterChain); } }); return registration; }
五、測試與驗證
5.1 測試類示例
@SpringBootTest @AutoConfigureMockMvc public class CorsTest { @Autowired private MockMvc mockMvc; @Test public void testCorsHeaders() throws Exception { mockMvc.perform(options("/api/users") .header("Access-Control-Request-Method", "GET") .header("Origin", "http://localhost:3000")) .andExpect(header().exists("Access-Control-Allow-Origin")) .andExpect(header().string("Access-Control-Allow-Methods", "GET")); } @Test public void testActualRequest() throws Exception { mockMvc.perform(get("/api/users") .header("Origin", "http://localhost:3000")) .andExpect(status().isOk()) .andExpect(header().string("Access-Control-Allow-Origin", "http://localhost:3000")); } }
5.2 使用CURL測試
檢查OPTIONS預(yù)檢請求:
curl -X OPTIONS http://localhost:8080/api/users \
-H "Origin: http://test.com" \
-H "Access-Control-Request-Method: GET" \
-I
檢查實際請求:
curl -X GET http://localhost:8080/api/users \
-H "Origin: http://test.com" \
-I
六、總結(jié)與推薦方案
6.1 配置方式對比
方式 | 適用場景 | 優(yōu)點 | 缺點 |
---|---|---|---|
全局WebMvcConfigurer | 大多數(shù)應(yīng)用 | 集中管理,支持細粒度配置 | 需要代碼變更 |
過濾器方式 | 需要最高優(yōu)先級處理 | 處理最早,避免被其他過濾器干擾 | 配置稍復(fù)雜 |
@CrossOrigin注解 | 特定接口需要特殊規(guī)則 | 精準(zhǔn)控制 | 分散在各處,維護成本高 |
屬性文件配置 | 簡單需求,配置驅(qū)動 | 無需代碼變更 | 靈活性較低 |
手動設(shè)置響應(yīng)頭 | 需要動態(tài)決定CORS頭 | 最大靈活性 | 代碼侵入性強 |
6.2 推薦方案
新項目:
- 使用WebMvcConfigurer全局配置
- 結(jié)合屬性文件動態(tài)配置允許的源
- 對特殊接口使用@CrossOrigin覆蓋全局設(shè)置
已有項目遷移:
- 先添加全局配置
- 逐步移除分散的注解配置
- 最終統(tǒng)一到1-2種管理方式
Spring Cloud微服務(wù):
- 在API Gateway統(tǒng)一處理CORS
- 各微服務(wù)禁用CORS或僅允許網(wǎng)關(guān)源
- 結(jié)合OAuth2等安全機制
6.3 終極建議
生產(chǎn)環(huán)境不要使用*作為允許源 - 明確列出可信域名
限制允許的方法和頭 - 按最小權(quán)限原則配置
合理設(shè)置maxAge - 平衡安全性和性能(建議1小時)
與前端團隊協(xié)作 - 確保雙方對CORS要求理解一致
監(jiān)控CORS錯誤 - 及時發(fā)現(xiàn)配置問題或惡意請求
通過合理配置CORS,可以在保障安全性的同時,為現(xiàn)代前后端分離架構(gòu)提供必要的跨域支持。Spring Boot提供了多種靈活的方式,開發(fā)者應(yīng)根據(jù)項目實際需求選擇最適合的方案。
到此這篇關(guān)于SpringBoot中處理跨域請求CORS的全面指南的文章就介紹到這了,更多相關(guān)SpringBoot跨域請求CORS內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis關(guān)閉一級緩存的兩種方式(分注解和xml兩種方式)
這篇文章主要介紹了MyBatis關(guān)閉一級緩存的兩種方式(分注解和xml兩種方式),mybatis默認開啟一級緩存,執(zhí)行2次相同sql,但是第一次查詢sql結(jié)果會加工處理這個時候需要關(guān)閉一級緩存,本文給大家詳細講解需要的朋友可以參考下2022-11-11詳解使用Spring AOP和自定義注解進行參數(shù)檢查
本篇文章主要介紹了詳解使用Spring AOP和自定義注解進行參數(shù)檢查,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04Spring事件監(jiān)聽器ApplicationListener源碼詳解
這篇文章主要介紹了Spring事件監(jiān)聽器ApplicationListener源碼詳解,ApplicationEvent以及Listener是Spring為我們提供的一個事件監(jiān)聽、訂閱的實現(xiàn),內(nèi)部實現(xiàn)原理是觀察者設(shè)計模式,需要的朋友可以參考下2023-05-05如何用Intellij idea2020打包jar的方法步驟
這篇文章主要介紹了如何用Intellij idea 2020打包jar的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04springboot中application.yml多環(huán)境生效規(guī)則說明
這篇文章主要介紹了springboot中application.yml多環(huán)境生效規(guī)則說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07