6種SpringBoot解決跨域請(qǐng)求的方法整理
一、跨域問(wèn)題簡(jiǎn)介
在Web開(kāi)發(fā)中,瀏覽器的同源策略(Same-Origin Policy)是一項(xiàng)重要的安全機(jī)制,它限制了一個(gè)源(Origin)中加載的文檔或腳本如何與另一個(gè)源的資源進(jìn)行交互。所謂同源,指的是協(xié)議、域名和端口號(hào)都相同。當(dāng)前端應(yīng)用試圖請(qǐng)求與自身不同源的后端API時(shí),就會(huì)遇到跨域問(wèn)題。
例如,當(dāng) http://frontend.com 的前端應(yīng)用嘗試訪問(wèn) http://backend.com/api 的后端服務(wù)時(shí),瀏覽器會(huì)阻止這種請(qǐng)求,并在控制臺(tái)報(bào)錯(cuò):
Access to XMLHttpRequest at 'http://backend.com/api' from origin 'http://frontend.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
跨域資源共享(CORS,Cross-Origin Resource Sharing)是一種標(biāo)準(zhǔn)機(jī)制,允許服務(wù)器聲明哪些源可以訪問(wèn)其資源。在SpringBoot應(yīng)用中,有多種方式可以解決跨域問(wèn)題,下面詳細(xì)介紹6種常見(jiàn)的解決方案。
二、方案一:基于@CrossOrigin注解的方法級(jí)別控制
這是最簡(jiǎn)單直接的方式,通過(guò)在Controller類或特定方法上添加@CrossOrigin注解來(lái)允許跨域請(qǐng)求。
實(shí)現(xiàn)方式
// 在方法級(jí)別允許跨域
@RestController
@RequestMapping("/api")
public class UserController {
@CrossOrigin(origins = "http://example.com")
@GetMapping("/users")
public List<User> getUsers() {
// 方法實(shí)現(xiàn)
return userService.findAll();
}
@GetMapping("/roles")
public List<Role> getRoles() {
// 此方法不允許跨域
return roleService.findAll();
}
}
// 在類級(jí)別允許跨域
@CrossOrigin(origins = {"http://example.com", "http://localhost:3000"})
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping
public List<Product> getAllProducts() {
// 方法實(shí)現(xiàn)
return productService.findAll();
}
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
// 方法實(shí)現(xiàn)
return productService.findById(id);
}
}
優(yōu)點(diǎn)
- 實(shí)現(xiàn)簡(jiǎn)單直觀
- 可以精確控制到方法級(jí)別
- 可以針對(duì)不同的API設(shè)置不同的CORS規(guī)則
缺點(diǎn)
- 代碼重復(fù),需要在多個(gè)地方添加注解
- 維護(hù)成本高,當(dāng)CORS策略變更時(shí),需要修改多處代碼
- 不適合大型項(xiàng)目中統(tǒng)一管理CORS策略
三、方案二:全局CORS配置(WebMvcConfigurer)
通過(guò)實(shí)現(xiàn)WebMvcConfigurer接口并重寫(xiě)addCorsMappings方法,可以在全局范圍內(nèi)配置CORS規(guī)則。
實(shí)現(xiàn)方式
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://example.com", "http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600); // 1小時(shí)內(nèi)不需要再預(yù)檢(發(fā)OPTIONS請(qǐng)求)
}
}
優(yōu)點(diǎn)
- 可以方便集中管理所有API的CORS配置
- 配置靈活,可以針對(duì)不同的URL模式設(shè)置不同的規(guī)則
- 代碼簡(jiǎn)潔,易于維護(hù)
缺點(diǎn)
- 在某些場(chǎng)景下可能需要與其他安全配置結(jié)合使用
四、方案三:使用CorsFilter
通過(guò)定義CorsFilter作為一個(gè)Bean,可以在過(guò)濾器級(jí)別處理跨域請(qǐng)求,這種方式比WebMvcConfigurer的優(yōu)先級(jí)更高。
實(shí)現(xiàn)方式
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 允許的源
config.addAllowedOrigin("http://example.com");
config.addAllowedOrigin("http://localhost:3000");
// 允許的HTTP方法
config.addAllowedMethod("*");
// 允許的頭信息
config.addAllowedHeader("*");
// 允許攜帶認(rèn)證信息(Cookie等)
config.setAllowCredentials(true);
// 預(yù)檢請(qǐng)求的有效期,單位為秒
config.setMaxAge(3600L);
// 對(duì)所有URL應(yīng)用這些配置
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
優(yōu)點(diǎn)
- 在過(guò)濾器級(jí)別處理,可以攔截所有請(qǐng)求
- 優(yōu)先級(jí)高于方案二
- 可以與其他過(guò)濾器組合使用
- 適合在不修改已有Controller的情況下添加CORS支持
缺點(diǎn)
- 無(wú)法精確到方法級(jí)別控制
- 對(duì)于復(fù)雜的規(guī)則可能不夠靈活
五、方案四:Spring Security中的CORS配置
如果項(xiàng)目使用了Spring Security,需要在Security配置中允許CORS,否則Security可能會(huì)攔截跨域請(qǐng)求。
實(shí)現(xiàn)方式
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults()) // 使用CorsConfigurationSource的默認(rèn)配置
.csrf().disable()
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://example.com", "http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
優(yōu)點(diǎn)
- 與Spring Security無(wú)縫集成
- 可以結(jié)合認(rèn)證和授權(quán)規(guī)則一起配置
- 適合需要安全控制的REST API
缺點(diǎn)
- 依賴Spring Security
- 對(duì)于不需要安全控制的簡(jiǎn)單應(yīng)用可能略顯復(fù)雜
六、方案五:網(wǎng)關(guān)層面解決跨域(Spring Cloud Gateway)
在微服務(wù)架構(gòu)中,可以在API網(wǎng)關(guān)層統(tǒng)一處理跨域問(wèn)題,這樣后端微服務(wù)就不需要各自配置CORS了。
實(shí)現(xiàn)方式
// Spring Cloud Gateway配置
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user_service_route", r -> r.path("/api/users/**")
.uri("lb://user-service"))
.route("product_service_route", r -> r.path("/api/products/**")
.uri("lb://product-service"))
.build();
}
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = ctx.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
headers.add("Access-Control-Allow-Headers", "Authorization, Content-Type");
headers.add("Access-Control-Allow-Credentials", "true");
headers.add("Access-Control-Max-Age", "3600");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
}
優(yōu)點(diǎn)
- 集中處理所有微服務(wù)的跨域問(wèn)題
- 后端服務(wù)無(wú)需關(guān)心跨域配置
- 便于統(tǒng)一管理和維護(hù)
- 適合微服務(wù)架構(gòu)
缺點(diǎn)
- 依賴Spring Cloud Gateway
- 配置相對(duì)復(fù)雜
- 對(duì)于單體應(yīng)用可能過(guò)于重量級(jí)
七、方案六:使用代理服務(wù)器
通過(guò)配置前端開(kāi)發(fā)服務(wù)器代理 (開(kāi)發(fā)環(huán)境) 或使用Nginx (生產(chǎn)環(huán)境) 等反向代理服務(wù)器,可以間接解決跨域問(wèn)題。這種方式實(shí)際上是繞過(guò)了瀏覽器的同源策略,而不是直接在后端解決CORS。
前端開(kāi)發(fā)服務(wù)器代理配置(以Vue CLI為例)
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': '/api'
}
}
}
}
}
Nginx反向代理配置
server {
listen 80;
server_name frontend.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://backend.example.com:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
優(yōu)點(diǎn)
- 完全繞過(guò)瀏覽器的同源策略限制
- 后端無(wú)需任何CORS配置
缺點(diǎn)
- 需要額外的代理配置
- 增加了系統(tǒng)復(fù)雜性
- 可能引入額外的網(wǎng)絡(luò)延遲
八、方案比較與選擇建議
| 方案 | 實(shí)現(xiàn)難度 | 靈活性 | 維護(hù)成本 | 適用場(chǎng)景 |
|---|---|---|---|---|
| @CrossOrigin注解 | 低 | 高 | 高 | 小型項(xiàng)目,特定API需要跨域 |
| WebMvcConfigurer | 中 | 中 | 低 | 大多數(shù)Spring Boot應(yīng)用 |
| CorsFilter | 中 | 中 | 低 | 需要優(yōu)先級(jí)高的CORS處理 |
| Spring Security | 高 | 高 | 中 | 有安全需求的應(yīng)用 |
| 網(wǎng)關(guān)層面解決 | 高 | 高 | 低 | 微服務(wù)架構(gòu) |
| 代理服務(wù)器 | 中 | 高 | 中 | 生產(chǎn)環(huán)境,嚴(yán)格的安全要求 |
九、最佳實(shí)踐與注意事項(xiàng)
1. 安全考慮
- 不要盲目設(shè)置
Access-Control-Allow-Origin: *,應(yīng)該明確指定允許的源 - 謹(jǐn)慎處理帶有憑證的請(qǐng)求(如Cookie),確保只允許受信任的源
- 對(duì)于敏感操作,考慮使用CSRF令牌進(jìn)行保護(hù)
2. 性能優(yōu)化
- 合理設(shè)置
Access-Control-Max-Age以減少預(yù)檢請(qǐng)求 - 避免在每個(gè)請(qǐng)求中都解析和構(gòu)建CORS頭
- 在網(wǎng)關(guān)層處理CORS可以減輕后端服務(wù)的負(fù)擔(dān)
3. 開(kāi)發(fā)與調(diào)試
- 在開(kāi)發(fā)環(huán)境可以適當(dāng)放寬CORS限制,但在生產(chǎn)環(huán)境一定要收緊
- 使用瀏覽器開(kāi)發(fā)者工具的Network面板調(diào)試CORS問(wèn)題
十、總結(jié)
跨域請(qǐng)求是前后端分離開(kāi)發(fā)中不可避免的問(wèn)題,Spring Boot提供了多種解決方案。從簡(jiǎn)單的@CrossOrigin注解到復(fù)雜的網(wǎng)關(guān)配置,我們可以根據(jù)項(xiàng)目規(guī)模和需求選擇合適的方案。在實(shí)際開(kāi)發(fā)中,建議綜合考慮安全性、靈活性和維護(hù)成本,選擇最適合項(xiàng)目的CORS解決方案。
對(duì)于大多數(shù)Spring Boot應(yīng)用,推薦使用全局CORS配置(WebMvcConfigurer)方案,它提供了良好的平衡性;而對(duì)于微服務(wù)架構(gòu),則推薦在網(wǎng)關(guān)層統(tǒng)一處理CORS問(wèn)題,以減少后端服務(wù)的配置負(fù)擔(dān)。
無(wú)論選擇哪種方案,都應(yīng)該遵循"最小權(quán)限原則" ,只允許必要的源訪問(wèn)必要的資源,確保系統(tǒng)的安全性。
以上就是6種SpringBoot解決跨域請(qǐng)求的方法整理的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot解決跨域請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何在Maven項(xiàng)目配置pom.xml指定JDK版本和編碼
maven是個(gè)項(xiàng)目管理工具,如果我們不告訴它要使用什么樣的jdk版本編譯,它就會(huì)用maven-compiler-plugin默認(rèn)的jdk版本來(lái)處理,這樣就容易出現(xiàn)版本不匹配的問(wèn)題,這篇文章主要給大家介紹了關(guān)于如何在Maven項(xiàng)目配置pom.xml指定JDK版本和編碼的相關(guān)資料,需要的朋友可以參考下2024-01-01
Java字符串相關(guān)類StringBuffer的用法詳解
java.lang包下的StringBuffer類,代表著可變的字符序列,可以用來(lái)對(duì)字符串內(nèi)容進(jìn)行增刪改操作。本文將通過(guò)示例詳細(xì)說(shuō)說(shuō)它的用法,感興趣的可以跟隨小編一起學(xué)習(xí)一下2022-10-10
高分面試從Hotspot源碼層面剖析java多態(tài)實(shí)現(xiàn)原理
這篇文章主要為大家介紹了在面試中從Hotspot源碼層面來(lái)剖析java多態(tài)的實(shí)現(xiàn)原理,這樣回答薪資隨你開(kāi),有需要的朋友可以借鑒參考下,希望大家多多加薪2022-01-01
Java實(shí)現(xiàn)簡(jiǎn)單字符生成器代碼例子
這篇文章主要介紹了Java實(shí)現(xiàn)簡(jiǎn)單字符生成器代碼例子,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-06-06
Maven添加Tomcat插件實(shí)現(xiàn)熱部署代碼實(shí)例
這篇文章主要介紹了Maven添加Tomcat插件實(shí)現(xiàn)熱部署代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
java如何讀取文件目錄返回樹(shù)形結(jié)構(gòu)
這篇文章主要介紹了java如何讀取文件目錄返回樹(shù)形結(jié)構(gòu)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

