description: "給出了解決跨域問題的一些方案"
date: 2022.10.02 10:34
categories:
- Web
tags: [Web, HTML, Spring]
keywords: CORS, fetch, @CrossFilter, Access-Control-Allow-Origin
原文地址:跨域(CORS)問題分析與解決方案
復現場景
新創建一個 VUE 工程,使用 fetch 函數(如下所示) 調用后臺 GET 接口,希望能夠在后臺獲得到響應。
fetch('http://127.0.0.1:8080/api/amber/userinfo/hello')
.then(response => response)
當瀏覽器訪問:http://127.0.0.1:8181/#/
就出現了問題:后臺能夠接到請求,但返回響應被瀏覽器攔截。
查看瀏覽器的 NETWORK 中顯示:
- Status code : 200
- Response 沒有返回正確結果
- Console 報錯:提示如下 :
Access to fetch at 'http://127.0.0.1:8080/api/amber/userinfo/hello' from origin 'http://127.0.0.1:8081' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
看報錯信息,顯然,遇到了跨域問題。根據瀏覽器策略,所以將請求攔截。
瀏覽器通過 URL 是否同源 判別我是否做了跨域的操作。
origin(源)
在 Origin 和 Site 中:
在 Web 中,origin(源)是指 協議(scheme)+ 主機名(host)+ 端口號(port)。
同源(Same origin),意味著 scheme/host/port 三元組完全相同。任意一部分不同,即為跨源(cross-origin,或稱為跨域)。
前臺地址:http://127.0.0.1:8181/#/
,后臺地址:http://127.0.0.1:8080/api/amber/userinfo/hello
,
其中 端口號(port) 不同,協議(scheme)和 主機名(host) 一致,可見不同源,且進行了跨源操作。
CORS
Cross-Origin Resource Sharing(CORS),跨源資源共享 或 跨域資源共享,是一種基于 HTTP Header 的機制,用來使服務端指定哪些其他的源可以從這個服務端加載資源。
CORS 在規范區分了預檢請求、簡單請求。了解 CORS 的工作原理,可以閱讀 CORS ,
或參閱 規范 以獲取更多詳細信息。
為了允許跨域請求,需要添加 CORS 相關的 HTTP Header。如果在請求的響應中未匹配到 CORS 的 Header,瀏覽器會拒絕它們。
CORS 解決方案
有時可能只有某一個或者部分接口允許跨域訪問,也可能所有接口都需要允許。
根據作用域不同,分為局部解決方案和全局解決方案。
一、局部設置 CORS
方法 1、響應上設置 CORS Header
在請求的響應頭上設置:Access-Control-Allow-Origin: *
。
@GetMapping("/hello")
@ResponseStatus(HttpStatus.OK)
public String getUser(HttpServletResponse response){
response.setHeader("Access-Control-Allow-Origin", "*");
return "hello, amber!";
}
方法 2、fetch 時關閉 CORS
在 fetch 請求上增加:mode: 'no-cors'
參數。
fetch('http://127.0.0.1:8080/api/amber/userinfo/hello', {mode: 'no-cors'})
.then(response => response)
方法3、@CrossOrigin
Spring 從 4.2 版本后開始支持 @CrossOrigin 注解實現跨域,
這在一定程度上簡化了我們實現跨域訪問的開發成本,
在需要跨域訪問的 方法 或者 類 加上該注解便可允許跨域訪問。
①、@CrossOrigin 基于方法級別使用,如下所示:
@RestController
@RequestMapping("/api/amber/userinfo")
public class UserInfoController {
@GetMapping("/hello")
@CrossOrigin
@ResponseStatus(HttpStatus.OK)
public String getUser(){
return "hello, amber!";
}
}
②、@CrossOrigin 在類級別也受支持,并由所有方法繼承。如下所示:
@CrossOrigin
@RestController
@RequestMapping("/api/amber/userinfo")
public class UserInfoController {
@GetMapping("/hello")
@ResponseStatus(HttpStatus.OK)
public String getUser(){
return "hello, amber!";
}
}
二、全局設置 CORS
除了細粒度、基于注解的配置之外,有時可能還需要全局 CORS 配置。
1、自定義 Filter/HandlerInterceptor
@WebFilter(filterName = "corsFilter", urlPatterns = {"/*"})
@Component
public class CORSConfig implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Credentials", String.valueOf(true));
res.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization,ybg");
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(request, response);
}
}
2、CorsFilter
Spring 4.2 版本后內置了一個 CorsFilter 專門用于處理 CORS 請求問題,所在包的位置:org.springframework.web.filter.CorsFilter
。
通過配置 CorsFilter 可實現基于 URL 級別控制跨域訪問的范圍。
CorsConfiguration config = new CorsConfiguration();
// Possibly...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
CorsFilter filter = new CorsFilter(source);