SpringBoot中處理跨域請(qǐng)求CORS的全面指南
一、CORS基礎(chǔ)概念
1.1 什么是跨域請(qǐng)求
跨域資源共享(Cross-Origin Resource Sharing, CORS)是一種安全機(jī)制,它允許Web應(yīng)用程序在一個(gè)域上的資源請(qǐng)求另一個(gè)域上的資源。瀏覽器出于安全考慮,會(huì)阻止不同源之間的AJAX請(qǐng)求,這是**同源策略(Same-Origin Policy)**的限制。
1.2 同源策略定義
兩個(gè)URL被認(rèn)為是同源的條件:
- 協(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ù)檢請(qǐng)求緩存時(shí)間
}
}
方式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 控制器方法級(jí)配置
方式3:使用@CrossOrigin注解
@RestController
@RequestMapping("/api")
public class UserController {
// 單個(gè)方法配置
@CrossOrigin(origins = "http://localhost:3000")
@GetMapping("/users")
public List<User> getUsers() {
// ...
}
// 整個(gè)控制器配置
@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)頭手動(dòng)設(shè)置
方式5:手動(dòng)添加響應(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ù)檢請(qǐng)求(Preflight Request)
對(duì)于"非簡(jiǎn)單請(qǐng)求",瀏覽器會(huì)先發(fā)送OPTIONS預(yù)檢請(qǐng)求:
簡(jiǎn)單請(qǐng)求條件:
1.使用GET、HEAD或POST方法
2.僅包含以下頭:
- Accept
- Accept-Language
- Content-Language
- Content-Type (僅限 application/x-www-form-urlencoded, multipart/form-data, text/plain)
非簡(jiǎn)單請(qǐng)求示例:
fetch('http://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({key: 'value'})
});
Spring Boot會(huì)自動(dòng)處理OPTIONS請(qǐng)求,無(wú)需開(kāi)發(fā)者額外編碼。
3.2 核心響應(yīng)頭說(shuō)明
| 響應(yīng)頭 | 說(shuō)明 |
|---|---|
| Access-Control-Allow-Origin | 允許訪問(wèn)的源,*表示任何源 |
| Access-Control-Allow-Methods | 允許的HTTP方法 |
| Access-Control-Allow-Headers | 允許的請(qǐng)求頭 |
| Access-Control-Expose-Headers | 允許瀏覽器訪問(wèn)的響應(yīng)頭 |
| Access-Control-Allow-Credentials | 是否允許發(fā)送Cookie和HTTP認(rèn)證信息 |
| Access-Control-Max-Age | 預(yù)檢請(qǐng)求結(jié)果的緩存時(shí)間(秒) |
3.3 常見(jiàn)問(wèn)題解決方案
問(wèn)題1:allowCredentials(true)與allowedOrigins("*")沖突
錯(cuò)誤:
When allowCredentials is true, allowedOrigins cannot contain the special value "*"
解決方案:
// 替換為具體域名
.allowedOrigins("http://localhost:3000", "https://example.com")
問(wèn)題2:前端仍然報(bào)CORS錯(cuò)誤
檢查步驟:
- 確保后端已正確配置
- 檢查瀏覽器控制臺(tái)錯(cuò)誤詳情
- 使用Postman等工具驗(yàn)證接口是否正常工作
- 檢查是否有多個(gè)CORS配置相互覆蓋
問(wèn)題3:自定義過(guò)濾器干擾CORS
解決方案:
確保CorsFilter在過(guò)濾器鏈中的優(yōu)先級(jí):
@Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistration() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(corsFilter());
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高優(yōu)先級(jí)
return registration;
}
四、安全最佳實(shí)踐
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時(shí),需要確保CORS配置在安全過(guò)濾器之前:
@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請(qǐng)求日志:
@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請(qǐng)求: {} {}", request.getMethod(), request.getRequestURI());
super.doFilterInternal(request, response, filterChain);
}
});
return registration;
}五、測(cè)試與驗(yàn)證
5.1 測(cè)試類示例
@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測(cè)試
檢查OPTIONS預(yù)檢請(qǐng)求:
curl -X OPTIONS http://localhost:8080/api/users \
-H "Origin: http://test.com" \
-H "Access-Control-Request-Method: GET" \
-I
檢查實(shí)際請(qǐng)求:
curl -X GET http://localhost:8080/api/users \
-H "Origin: http://test.com" \
-I
六、總結(jié)與推薦方案
6.1 配置方式對(duì)比
| 方式 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| 全局WebMvcConfigurer | 大多數(shù)應(yīng)用 | 集中管理,支持細(xì)粒度配置 | 需要代碼變更 |
| 過(guò)濾器方式 | 需要最高優(yōu)先級(jí)處理 | 處理最早,避免被其他過(guò)濾器干擾 | 配置稍復(fù)雜 |
| @CrossOrigin注解 | 特定接口需要特殊規(guī)則 | 精準(zhǔn)控制 | 分散在各處,維護(hù)成本高 |
| 屬性文件配置 | 簡(jiǎn)單需求,配置驅(qū)動(dòng) | 無(wú)需代碼變更 | 靈活性較低 |
| 手動(dòng)設(shè)置響應(yīng)頭 | 需要?jiǎng)討B(tài)決定CORS頭 | 最大靈活性 | 代碼侵入性強(qiáng) |
6.2 推薦方案
新項(xiàng)目:
- 使用WebMvcConfigurer全局配置
- 結(jié)合屬性文件動(dòng)態(tài)配置允許的源
- 對(duì)特殊接口使用@CrossOrigin覆蓋全局設(shè)置
已有項(xiàng)目遷移:
- 先添加全局配置
- 逐步移除分散的注解配置
- 最終統(tǒng)一到1-2種管理方式
Spring Cloud微服務(wù):
- 在API Gateway統(tǒng)一處理CORS
- 各微服務(wù)禁用CORS或僅允許網(wǎng)關(guān)源
- 結(jié)合OAuth2等安全機(jī)制
6.3 終極建議
生產(chǎn)環(huán)境不要使用*作為允許源 - 明確列出可信域名
限制允許的方法和頭 - 按最小權(quán)限原則配置
合理設(shè)置maxAge - 平衡安全性和性能(建議1小時(shí))
與前端團(tuán)隊(duì)協(xié)作 - 確保雙方對(duì)CORS要求理解一致
監(jiān)控CORS錯(cuò)誤 - 及時(shí)發(fā)現(xiàn)配置問(wèn)題或惡意請(qǐng)求
通過(guò)合理配置CORS,可以在保障安全性的同時(shí),為現(xiàn)代前后端分離架構(gòu)提供必要的跨域支持。Spring Boot提供了多種靈活的方式,開(kāi)發(fā)者應(yīng)根據(jù)項(xiàng)目實(shí)際需求選擇最適合的方案。
到此這篇關(guān)于SpringBoot中處理跨域請(qǐng)求CORS的全面指南的文章就介紹到這了,更多相關(guān)SpringBoot跨域請(qǐng)求CORS內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)組的動(dòng)態(tài)初始化和常見(jiàn)問(wèn)題解析
本文介紹了數(shù)組動(dòng)態(tài)初始化的概念,即在初始化時(shí)僅指定數(shù)組長(zhǎng)度,系統(tǒng)會(huì)為數(shù)組分配初始值,而靜態(tài)初始化則手動(dòng)指定數(shù)組元素,系統(tǒng)根據(jù)元素個(gè)數(shù)計(jì)算數(shù)組長(zhǎng)度,這兩種初始化方式應(yīng)用場(chǎng)景不同,另外,還講述了數(shù)組默認(rèn)初始化值的規(guī)律及數(shù)組常見(jiàn)問(wèn)題,如越界問(wèn)題等2024-10-10
SpringBoot學(xué)習(xí)篇之@Valid與@Validated的區(qū)別
@Valid是使用Hibernate?validation的時(shí)候使用,@Validated是只用Spring?Validator校驗(yàn)機(jī)制使用,下面這篇文章主要給大家介紹了關(guān)于SpringBoot學(xué)習(xí)篇之@Valid與@Validated區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-11-11
Java內(nèi)存各部分OOM出現(xiàn)原因及解決方法(必看)
下面小編就為大家?guī)?lái)一篇Java內(nèi)存各部分OOM出現(xiàn)原因及解決方法(必看)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
解決bean對(duì)象注入報(bào)錯(cuò):Field in required a bean&nb
這篇文章主要介紹了解決bean對(duì)象注入報(bào)錯(cuò):Field in required a bean of type‘‘that could not be found.問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-06-06
Java樹(shù)形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件方法記錄
最近好像得罪了poi,遇到的都是導(dǎo)出word、Excel、pdf的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于Java樹(shù)形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10

