使用 SpringBoot 解決跨域訪問的多種實(shí)現(xiàn)方式
在當(dāng)今的 Web 應(yīng)用開發(fā)中,跨域訪問問題是一個常見且關(guān)鍵的挑戰(zhàn)??缬蛟L問指的是當(dāng)一個網(wǎng)頁中的腳本(通常是 JavaScript)試圖從一個與當(dāng)前網(wǎng)頁的源(包括域名、協(xié)議和端口)不同的服務(wù)器獲取資源或進(jìn)行數(shù)據(jù)交互時所面臨的限制。這種限制是由瀏覽器的同源策略所施加的。
同源策略的核心原則是確保只有來自相同源的網(wǎng)頁能夠相互訪問和交互數(shù)據(jù)。其主要目的是保護(hù)用戶的隱私和安全,防止惡意網(wǎng)站通過腳本獲取用戶在其他網(wǎng)站上的敏感信息。
然而,在許多現(xiàn)代的 Web 應(yīng)用架構(gòu)中,如前后端分離的模式,前端頁面可能運(yùn)行在一個域名(例如:www.frontend.com),而后端服務(wù)則部署在另一個域名(例如:api.backend.com)。這種架構(gòu)上的分離導(dǎo)致了跨域訪問的需求。
當(dāng)發(fā)生跨域請求時,瀏覽器會在發(fā)送請求前進(jìn)行預(yù)檢(Preflight)操作。預(yù)檢請求使用 OPTIONS 方法發(fā)送,以獲取服務(wù)器對跨域請求的許可信息,包括允許的請求方法、請求頭和其他相關(guān)權(quán)限。如果服務(wù)器的響應(yīng)不符合瀏覽器的預(yù)期,跨域請求將被阻止。
跨域訪問問題不僅影響到簡單的數(shù)據(jù)獲取,還可能涉及到復(fù)雜的操作,如發(fā)送 POST 請求、攜帶自定義請求頭或需要使用 Cookie 進(jìn)行身份驗(yàn)證等。解決跨域問題需要綜合考慮安全性、性能和可擴(kuò)展性等多個方面,以確保在滿足業(yè)務(wù)需求的同時,不會引入新的安全風(fēng)險(xiǎn)。
項(xiàng)目創(chuàng)建及依賴配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>cors-solution</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CORS Solution</name>
<properties>
<java.version>19</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件(application.yml)
server:
port: 8080
cors:
allowed-origins: http://your-frontend-domain.com
allowed-methods: *
allowed-headers: *
allow-credentials: true
跨域配置類(方式一:使用全局配置)
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 CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 從配置文件中讀取允許的源
config.setAllowedOrigins(Arrays.asList(
Objects.requireNonNull(this.getProperties().get("cors.allowed-origins"))
.toString().split(",")));
// 從配置文件中讀取允許的方法
config.setAllowedMethods(Arrays.asList(
Objects.requireNonNull(this.getProperties().get("cors.allowed-methods"))
.toString().split(",")));
// 從配置文件中讀取允許的請求頭
config.setAllowedHeaders(Arrays.asList(
Objects.requireNonNull(this.getProperties().get("cors.allowed-headers"))
.toString().split(",")));
// 從配置文件中讀取是否允許攜帶憑證
config.setAllowCredentials(Boolean.parseBoolean(
Objects.requireNonNull(this.getProperties().get("cors.allow-credentials"))
.toString()));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
private Properties getProperties() {
return new PropertiesLoaderUtils().loadProperties(new ClassPathResource("application.yml"));
}
}
控制器類(提供測試接口)
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "跨域測試成功!";
}
}
前端頁面(index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跨域測試</title>
</head>
<body>
<h1>跨域測試頁面</h1>
<button onclick="fetchData()">獲取數(shù)據(jù)</button>
<script>
function fetchData() {
fetch('http://your-backend-domain.com/test')
.then(response => response.text())
.then(data => {
console.log(data);
})
.catch(error => console.error('錯誤:', error));
}
</script>
</body>
</html>
方式二:在控制器方法上使用注解
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@CrossOrigin(origins = "http://your-frontend-domain.com")
@GetMapping("/test")
public String test() {
return "跨域測試成功!";
}
}
其他方法
除了上述兩種常見的方法外,還可以通過配置 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 CorsConfigurer implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 從配置文件中讀取相關(guān)屬性
registry.addMapping("/**")
.allowedOrigins(Arrays.asList(
Objects.requireNonNull(this.getProperties().get("cors.allowed-origins"))
.toString().split(",")))
.allowedMethods(Arrays.asList(
Objects.requireNonNull(this.getProperties().get("cors.allowed-methods"))
.toString().split(",")))
.allowedHeaders(Arrays.asList(
Objects.requireNonNull(this.getProperties().get("cors.allowed-headers"))
.toString().split(",")))
.allowCredentials(Boolean.parseBoolean(
Objects.requireNonNull(this.getProperties().get("cors.allow-credentials"))
.toString()));
}
private Properties getProperties() {
return new PropertiesLoaderUtils().loadProperties(new ClassPathResource("application.yml"));
}
}
總結(jié)
通過以上多種方式,我們可以在 SpringBoot 項(xiàng)目中有效地解決跨域訪問問題。在實(shí)際開發(fā)中,可以根據(jù)項(xiàng)目的具體需求和架構(gòu)選擇合適的方式。全局配置適用于整個應(yīng)用的所有接口,在控制器方法上使用注解則可以更靈活地控制特定接口的跨域策略,而通過配置 WebMvcConfigurer 也是一種可行的選擇。