大家好,我是 V 哥,跨域問題是應用開發中比較常見的問題,比如前端應用和後端業務的端口不同,前端要向後端發送請求來獲取數據,這個時候就會產生跨域問題,V 哥先從跨域問題的產生開始,來詳細介紹跨域問題及解決方案。
CORS跨域問題的產生原因
CORS(Cross-Origin Resource Sharing,跨域資源共享)跨域問題源於瀏覽器的同源策略。同源策略是瀏覽器的一種安全機制,它要求瀏覽器在訪問一個資源時,該資源的協議、域名和端口必須與當前頁面的協議、域名和端口完全一致,否則就會被視為跨域請求,瀏覽器會對這類請求進行限制。
例如,當前頁面的 URL 是 http://www.weige.com:8080,如果該頁面向 http://api.weige2.com:9090 發起請求,由於域名(www.weige.com 和 api.weige2.com)和端口(8080 和 9090)不同,就會觸發跨域問題。
Spring Boot 處理 CORS 跨域問題的方法及案例
業務場景
假設我們有一個前端應用運行在 http://localhost:3000,這是一個使用 React 構建的單頁面應用。後端使用 Spring Boot 開發,運行在 http://localhost:8080。前端需要向後端的 /api/users 接口發送請求來獲取用户列表,由於前端和後端的端口不同,會產生跨域問題。
解決方法
1. 使用 @CrossOrigin 註解
@CrossOrigin 註解可以用於控制器類或控制器方法上,用於允許跨域請求。
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
@RestController
// 允許來自 http://localhost:3000 的跨域請求
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
@GetMapping("/api/users")
public List<String> getUsers() {
return Arrays.asList("Alice", "Bob", "Charlie");
}
}
在上述代碼中,@CrossOrigin(origins = "http://localhost:3000") 註解表示允許來自 http://localhost:3000 的跨域請求訪問該控制器中的所有方法。如果只需要允許某個方法跨域,可以將該註解添加到具體的方法上。
除了使用 @CrossOrigin 註解外,在 Spring Boot 中還可以通過以下幾種方式解決 CORS 跨域問題:
2. 實現 WebMvcConfigurer 接口進行全局配置
通過實現 WebMvcConfigurer 接口並重寫 addCorsMappings 方法,可以對整個 Spring Boot 應用進行全局的 CORS 配置。
示例代碼
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://weige.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
代碼解釋
addMapping("/**"):表示對所有的請求路徑都應用 CORS 配置。allowedOrigins("http://weige.com"):指定允許訪問的源站,這裏僅允許http://weige.com進行跨域訪問。如果需要允許多個源站,可以傳入多個參數。allowedMethods("GET", "POST", "PUT", "DELETE"):指定允許的請求方法。allowedHeaders("*"):允許所有的請求頭。allowCredentials(true):允許攜帶憑證(如 Cookie)。maxAge(3600):預檢請求的有效期,單位為秒。
3. 使用過濾器(Filter)
創建一個自定義的過濾器,在過濾器中設置 CORS 相關的響應頭,以允許跨域請求。
示例代碼
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://weige.com");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
}
代碼解釋
- 在
doFilter方法中,設置了一系列 CORS 相關的響應頭,與前面通過WebMvcConfigurer配置的含義類似。 - 對於
OPTIONS請求(預檢請求),直接返回 200 狀態碼,避免後續的業務邏輯處理。
4. 使用 Spring Security 進行 CORS 配置
如果項目中使用了 Spring Security,也可以在 Spring Security 的配置中進行 CORS 配置。
示例代碼
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource())
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.csrf().disable();
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://weige.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
代碼解釋
- 在
securityFilterChain方法中,通過cors().configurationSource(corsConfigurationSource())引入自定義的 CORS 配置。 corsConfigurationSource方法創建了一個CorsConfigurationSource實例,並設置了允許的源站、請求方法、請求頭、憑證等信息。
這些方法都可以有效地解決 Spring Boot 中的 CORS 跨域問題,你可以根據項目的具體需求和架構選擇合適的方式。
CORS跨域問題是否會影響網站的性能?
CORS跨域問題本身通常不會直接影響網站的核心性能指標,如響應時間、吞吐量等,但在處理CORS跨域的過程中以及由於跨域可能引發的一些情況,可能會對網站性能產生間接影響,具體如下:
增加請求次數
- 原因:在跨域請求時,瀏覽器可能會先發送預檢請求(OPTIONS)來詢問服務器是否允許該跨域請求。這額外的請求會增加網絡往返次數,消耗一定的時間和網絡資源。
- 影響:對於性能敏感的應用,特別是在移動網絡等帶寬有限、延遲較高的環境下,額外的請求可能會明顯增加整體的請求響應時間,導致用户等待時間變長,影響用户體驗。例如,一個實時數據更新的網頁,若頻繁進行跨域請求且每次都有預檢請求,可能會使數據更新出現明顯延遲。
增加服務器負載
- 原因:服務器需要處理額外的預檢請求以及對跨域請求進行相應的配置和驗證等操作。如果跨域請求量較大,服務器需要消耗更多的資源來處理這些請求,包括CPU、內存等。
- 影響:可能會導致服務器負載增加,響應速度變慢。當服務器負載過高時,甚至可能影響到其他正常請求的處理,導致整個網站的性能下降,出現響應卡頓、超時等問題。比如電商網站在促銷活動期間,大量用户同時訪問跨域的商品詳情頁等,若跨域處理不當,可能加重服務器負擔,影響頁面加載速度。
緩存策略受限
- 原因:由於跨域請求的複雜性,瀏覽器對跨域資源的緩存策略可能會受到限制。不同的瀏覽器對於跨域緩存的處理方式有所不同,可能會導致緩存命中率降低。
- 影響:緩存命中率降低意味着瀏覽器需要更頻繁地從服務器獲取資源,增加了網絡流量和服務器負載,也會使頁面加載速度變慢。例如,一個依賴跨域加載大量靜態資源的網頁,如果緩存受限,每次刷新頁面都需要重新獲取資源,會大大降低頁面的加載性能。
安全機制導致性能損耗
- 原因:為了確保跨域請求的安全性,服務器和瀏覽器都需要執行一系列的安全檢查和驗證機制,如檢查請求頭中的源信息、驗證憑證等。
- 影響:這些操作會消耗一定的計算資源和時間,在一定程度上可能會影響請求的處理速度和網站性能。不過,這種影響通常相對較小,在大多數情況下不會成為性能瓶頸,但在高併發、高性能要求的場景下,也可能會產生一定的累積效應。
最後
所以什麼是跨域問題呢,V 哥來小結一下,跨域問題源於瀏覽器的同源策略,即瀏覽器在訪問資源時要求該資源的協議、域名和端口與當前頁面完全一致,否則視為跨域請求並進行限制。產生的原因是:不同源(協議、域名、端口任意一項不同)的頁面之間進行資源請求時觸發,如前端 http://localhost:3000 向 http://localhost:8080 的後端發起請求。解決方法可以使用註解方式或全局配置。需要注意的是,跨域本身不直接影響性能,但處理過程及相關情況會帶來間接影響。關注威哥愛編程,全棧之路就你行。