1.什么是跨域?
跨域是瀏覽器為了保障安全而遵循的一種規則,是同源策略的一部分。
-
同源:要求協議、域名、端口三者完全相同。
-
跨域:只要協議、域名、端口中有任何一個不同,瀏覽器就會判定為跨域請求。
跨域(Cross-Origin)是瀏覽器獨有的安全策略,不存在于安卓、iOS、Node.js、Python、Java 等原生客戶端或服務器端環境中。因為瀏覽器是一個開放的、執行不可信代碼,也就是各個網站的 js 腳本的環境,同源策略是為了保證用戶的信息安全。
所以,如果平時測試接口用的是 postman 發送請求,不需要關心跨域問題,但是如果是前后端聯調就必須處理跨域問題。
2.瀏覽器處理跨域請求的方式
瀏覽器遵循同源策略,不允許頁面獲取跨域請求返回的響應結果。
比如:當前網頁是?http://127.0.0.1:63342,然后向服務器?http://127.0.0.1:8080?發送 GET 請求獲取數據。整個過程分成三步:
-
瀏覽器發送請求
-
服務器接收請求,處理業務,返回響應
-
瀏覽器獲取服務器返回的響應并根據響應渲染頁面
無論是跨域請求還是非跨域請求,瀏覽器都可以發送給服務器,并且接收服務器返回的響應數據。
-
如果該請求是非跨域請求,則 js 腳本可以訪問響應數據
-
如果該請求是跨域請求,瀏覽器會攔截響應數據,不讓 js 訪問這些數據
其實瀏覽器可以向不同的域名發送請求,但是瀏覽器會攔截響應內容,不讓 js 訪問,無論請求是否成功。
3.跨域處理方案
跨域處理的核心:讓瀏覽器不要攔截跨域請求返回的數據。
服務器的響應中如果有 Access-Control-Allow-Origin: * 這個響應頭,就是告訴瀏覽器:"我是服務器,雖然我跟這個網頁不是同源的,但是我允許這個網頁跟我通信,我們之間的通信是安全的",瀏覽器就不會攔截 js 對響應數據的訪問。
瀏覽器發送的請求可以分為簡單請求和復雜請求:
-
如果是簡單請求,則瀏覽器直接發送。
-
如果是復雜請求,則瀏覽器先發送一個預檢請求,即 OPTIONS 請求,問一句:"我可以發送一個超級復雜的跨域請求嗎?",服務器需要返回針對 OPTIONS 的響應。如果服務器允許發送這個復雜請求,瀏覽器才會真正發送請求。
常見的跨域處理方案有:代理服務器、后端服務器跨域配置。
3.1代理服務器
瀏覽器將請求發送到跟頁面同源的代理服務器,代理服務器再將請求轉發到目標服務器。因為服務器間通信不受同源策略限制。比如常見的用 nginx 作為代理服務器。
請求處理過程:
-
前端發送請求,請求經過 nginx,請求被轉發到后端服務器。
-
服務器返回原始響應,原始響應經過 nginx,nginx 自動添加 Access-Control-Allow-Origin: * 響應頭,響應返回給前端,js 可以訪問響應數據。
# nginx.conf
server {listen 63342;# 前端頁面的域名或ipserver_name 127.0.0.1;# 代理所有以 /api/ 開頭的請求到后端服務器location /api/ {# 后端服務器地址proxy_pass http://127.0.0.1:8080/; # 修改請求頭,確保后端能收到正確的原始主機信息proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;# 添加CORS響應頭,允許所有來源的請求,生產環境應關閉add_header 'Access-Control-Allow-Origin' '*'; # 允許的請求方法add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';if ($request_method = 'OPTIONS') {# 對于OPTIONS請求,直接返回204狀態碼,不需要轉發到后端return 204;}}
}
3.2 后端跨域配置
3.2.1 配置 CORS
配置全局 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 WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 允許所有接口跨域.allowCredentials(true) // 允許瀏覽器在跨域請求中發送認證信息.allowedOriginPatterns("*") // 允許訪問資源的源(協議、域名、端口).allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允許的方法.allowedHeaders("*") // 允許的請求頭.exposedHeaders("*"); // 哪些響應頭可以暴露給前端js}
}
3.2.2 提供 CorsFilter
提供一個 CorsFilter 的 Bean 作為過濾器。
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 MyCorsFilter {@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.addAllowedOriginPattern("*"); // 放行所有域名,生產環境請對此進行修改config.setAllowCredentials(true); // 是否發送cookieconfig.addAllowedMethod("*"); // 放行的請求方式config.addAllowedHeader("*"); // 放行的請求頭config.addExposedHeader("*"); // 暴露頭部信息// UrlBasedCorsConfigurationSource: 可以為不同的URL路徑設置不同的CORS規則UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();// 所有的URL路徑都使用同一個CORS規則corsConfigurationSource.registerCorsConfiguration("/**", config);return new CorsFilter(corsConfigurationSource);}
}
3.2.3 @CrossOrigin 注解
在接口類上或者接口方法上添加 @CrossOrigin 注解,表示整個類、單個接口的響應不會被攔截。
@RestController
@CrossOrigin
public class DemoController {@PutMapping("/put")public Integer put(MultipartFile file) {System.out.println(file.getOriginalFilename());return 200;}@GetMapping("/get")public Integer get() {return 200;}
}
文章轉載自:ningqw
原文鏈接:SpringBoot 常用跨域處理方案 - ningqw - 博客園
體驗地址:JNPF快速開發平臺