【轉】跨域(CORS)問題分析與解決方案


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);
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容