SpringBoot處理跨域請(qǐng)求(CORS)的五種方式
一、CORS基礎(chǔ)概念
1. 什么是跨域請(qǐng)求?
當(dāng)瀏覽器從一個(gè)域名的網(wǎng)頁(yè)去請(qǐng)求另一個(gè)域名的資源時(shí),如果域名、端口或協(xié)議不同,就會(huì)產(chǎn)生跨域請(qǐng)求。出于安全考慮,瀏覽器默認(rèn)會(huì)阻止這類請(qǐng)求。
2. 簡(jiǎn)單請(qǐng)求 vs 預(yù)檢請(qǐng)求
| 類型 | 條件 | 處理方式 |
|---|---|---|
| 簡(jiǎn)單請(qǐng)求 | GET/HEAD/POST方法,且Content-Type為text/plain、multipart/form-data或application/x-www-form-urlencoded | 直接發(fā)送請(qǐng)求,帶Origin頭 |
| 預(yù)檢請(qǐng)求(OPTIONS) | 不符合簡(jiǎn)單請(qǐng)求條件的其他請(qǐng)求 | 先發(fā)送OPTIONS請(qǐng)求,獲得許可后再發(fā)送實(shí)際請(qǐng)求 |
二、Spring Boot處理CORS的5種方式
1. 使用@CrossOrigin注解
適用場(chǎng)景:針對(duì)單個(gè)控制器或方法級(jí)別的CORS配置
@RestController
@RequestMapping("/api")
public class MyController {
// 允許特定源的跨域訪問(wèn)
@CrossOrigin(origins = "https://example.com")
@GetMapping("/resource")
public ResponseEntity<String> getResource() {
return ResponseEntity.ok("跨域資源");
}
// 更詳細(xì)的配置
@CrossOrigin(origins = {"https://example.com", "https://api.example.com"},
allowedHeaders = {"Content-Type", "Authorization"},
methods = {RequestMethod.GET, RequestMethod.POST},
maxAge = 3600)
@PostMapping("/save")
public ResponseEntity<String> saveResource() {
return ResponseEntity.ok("保存成功");
}
}
2. 全局CORS配置
適用場(chǎng)景:應(yīng)用級(jí)別的統(tǒng)一CORS配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 匹配的路徑
.allowedOrigins("https://example.com", "https://api.example.com") // 允許的源
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允許的方法
.allowedHeaders("*") // 允許的請(qǐng)求頭
.exposedHeaders("Authorization", "Content-Disposition") // 暴露的響應(yīng)頭
.allowCredentials(true) // 是否允許發(fā)送cookie
.maxAge(3600); // 預(yù)檢請(qǐng)求緩存時(shí)間(秒)
// 可以添加多個(gè)配置
registry.addMapping("/public/**")
.allowedOrigins("*");
}
}
3. 使用Filter處理CORS
適用場(chǎng)景:需要更底層控制或與非Spring Web環(huán)境集成
@Configuration
public class CorsFilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 配置CORS規(guī)則
config.setAllowCredentials(true);
config.addAllowedOrigin("https://example.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setMaxAge(3600L);
// 對(duì)所有路徑生效
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean =
new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 設(shè)置最高優(yōu)先級(jí)
return bean;
}
}
4. Spring Security中的CORS配置
適用場(chǎng)景:使用Spring Security的項(xiàng)目
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and() // 啟用CORS支持
.csrf().disable() // 通常CORS和CSRF不能同時(shí)使用
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated();
}
// 提供CORS配置源
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowedHeaders(Arrays.asList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
5. 響應(yīng)頭手動(dòng)設(shè)置
適用場(chǎng)景:需要?jiǎng)討B(tài)控制CORS頭
@RestController
public class DynamicCorsController {
@GetMapping("/dynamic-cors")
public ResponseEntity<String> dynamicCors(HttpServletRequest request,
HttpServletResponse response) {
// 根據(jù)請(qǐng)求動(dòng)態(tài)設(shè)置CORS頭
String origin = request.getHeader("Origin");
if (isAllowedOrigin(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, POST");
}
return ResponseEntity.ok("動(dòng)態(tài)CORS響應(yīng)");
}
private boolean isAllowedOrigin(String origin) {
// 實(shí)現(xiàn)你的源驗(yàn)證邏輯
return origin != null && origin.endsWith("example.com");
}
}
三、CORS配置詳解
1. 核心響應(yīng)頭說(shuō)明
| 響應(yīng)頭 | 說(shuō)明 |
|---|---|
| Access-Control-Allow-Origin | 允許訪問(wèn)的源,可以是具體域名或*(不推薦使用*,特別是需要憑證時(shí)) |
| Access-Control-Allow-Methods | 允許的HTTP方法(GET, POST等) |
| Access-Control-Allow-Headers | 允許的請(qǐng)求頭 |
| Access-Control-Expose-Headers | 瀏覽器可以訪問(wèn)的響應(yīng)頭 |
| Access-Control-Allow-Credentials | 是否允許發(fā)送cookie(true/false),設(shè)為true時(shí)Allow-Origin不能為* |
| Access-Control-Max-Age | 預(yù)檢請(qǐng)求結(jié)果的緩存時(shí)間(秒) |
2. 常見(jiàn)問(wèn)題解決方案
問(wèn)題1:預(yù)檢請(qǐng)求(OPTIONS)被攔截
解決方案:
- 確保OPTIONS請(qǐng)求不被安全框架攔截
- 在Spring Security中配置:
http.cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
問(wèn)題2:帶憑證的請(qǐng)求失敗
解決方案:
- 確保
allowCredentials(true)和具體的allowedOrigins(不能是*) - 前端需要設(shè)置
withCredentials: true(如axios)
問(wèn)題3:特定響應(yīng)頭無(wú)法獲取
解決方案:
- 使用
exposedHeaders暴露需要的頭:
.exposedHeaders("Custom-Header", "Authorization")
四、最佳實(shí)踐建議
- 生產(chǎn)環(huán)境不要使用通配符*:明確指定允許的源
- 合理限制HTTP方法:只開(kāi)放必要的方法(GET/POST等)
- 考慮使用環(huán)境變量:動(dòng)態(tài)配置允許的源
@Value("${cors.allowed.origins}")
private String[] allowedOrigins;
// 在配置中使用
.allowedOrigins(allowedOrigins)
- 結(jié)合安全框架:Spring Security項(xiàng)目使用專門(mén)的CORS配置
- 測(cè)試不同場(chǎng)景:簡(jiǎn)單請(qǐng)求和預(yù)檢請(qǐng)求都要測(cè)試
五、完整配置示例
@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
@Value("${app.cors.allowed-origins}")
private String[] allowedOrigins;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins(allowedOrigins)
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("Authorization", "Content-Disposition")
.allowCredentials(true)
.maxAge(3600);
registry.addMapping("/public/**")
.allowedOrigins("*")
.allowedMethods("GET", "OPTIONS");
}
// 可選:提供CORS過(guò)濾器作為備選
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.applyPermitDefaultValues();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList(allowedOrigins));
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
六、總結(jié)
Spring Boot提供了多種靈活的方式來(lái)處理CORS:
- 簡(jiǎn)單場(chǎng)景:使用
@CrossOrigin注解 - 統(tǒng)一配置:實(shí)現(xiàn)
WebMvcConfigurer的addCorsMappings方法 - 底層控制:配置
CorsFilter - 安全項(xiàng)目:結(jié)合Spring Security的
cors()配置 - 動(dòng)態(tài)需求:手動(dòng)設(shè)置響應(yīng)頭
根據(jù)項(xiàng)目需求選擇合適的方式,并遵循安全最佳實(shí)踐,可以有效地解決跨域問(wèn)題,同時(shí)保證應(yīng)用的安全性。
以上就是SpringBoot處理跨域請(qǐng)求(CORS)的五種方式的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot處理跨域請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一個(gè)Java中BigDecimal的問(wèn)題記錄
這篇文章主要給大家介紹了關(guān)于Java中一個(gè)BigDecimal問(wèn)題的相關(guān)資料,通過(guò)文中介紹的方法可以很方便的解決BigDecimal進(jìn)行計(jì)算的時(shí)候不管怎么計(jì)算,最后得到的值都沒(méi)有變化的問(wèn)題,需要的朋友可以參考下2021-11-11
mybatis-plus 查詢時(shí)排除字段方法的兩種方法
我們?cè)陂_(kāi)發(fā)應(yīng)用時(shí),在某些應(yīng)用場(chǎng)景下查詢有時(shí)需要排除某些字段,本文主要介紹了兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
Java通過(guò)URL獲取公眾號(hào)文章生成HTML的方法
這篇文章主要介紹了Java通過(guò)URL獲取公眾號(hào)文章生成HTML的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
SpringBoot使用@SpringBootTest注解開(kāi)發(fā)單元測(cè)試教程
這篇文章主要介紹了SpringBoot使用@SpringBootTest注解開(kāi)發(fā)單元測(cè)試教程,本文通過(guò)詳細(xì)的案例過(guò)程來(lái)說(shuō)明如何使用該項(xiàng)技術(shù),需要的朋友可以參考下2021-06-06
SpringBoot關(guān)閉過(guò)程中銷毀DisposableBean解讀
這篇文章主要介紹了SpringBoot關(guān)閉過(guò)程中銷毀DisposableBean解讀,一個(gè)bean的生命周期,指的是 bean 從創(chuàng)建,初始化,一系列使用,銷毀的過(guò)程,今天來(lái)講講 bean 的初始化和銷毀的方法,需要的朋友可以參考下2023-12-12
java 基礎(chǔ)教程之多線程詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了java 基礎(chǔ)教程之多線程詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,線程的基本屬性、如何創(chuàng)建線程、線程的狀態(tài)切換以及線程通信,需要的朋友可以參考下2017-03-03
Java設(shè)計(jì)模式之備忘錄模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院
我們?cè)诰幊痰臅r(shí)候,經(jīng)常需要保存對(duì)象的中間狀態(tài),當(dāng)需要的時(shí)候,可以恢復(fù)到這個(gè)狀態(tài)。接下來(lái)通過(guò)本文給大家分享java設(shè)計(jì)模式之備忘錄模式,感興趣的的朋友一起看看吧2017-08-08

