跨域問題(CORS / Access-Control-Allow-Origin)

xcbeyond發表於2018-11-24

1、前言

      最近在專案中,呼叫Eureka REST介面時,出現了CORS跨越問題(Cross-origin resource sharing),在此與大家進行分享,避免多走些彎路。

      專案前端(http://localhost:9000)通過Ajax方式呼叫Eureka REST 介面(http://localhost:8761/eureka/apps)時,卻沒有任何反應,則通過F12檢視日誌發現出現“Access-Control-Allow-Origin“類 異常,詳細如下:

…… http://localhost:8761/eureka/apps. Origin http://localhost:9000 is not allowed by Access-Control-Allow-Origin……

通過google,發現是由於CORS跨越問題造成的,解決辦法無非有兩種方式:響應頭新增引數和新增過濾器,下面就詳細說說CORS跨越問題的起因與詳細解決辦法。

2、CORS

     CORS,常被大家稱之為跨越問題,準確的叫法是跨域資源共享(CORS,Cross-origin resource sharing),是W3C標準,是一種機制,它使用額外的HTTP頭來告訴瀏覽器 讓執行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域請求一個資源時,資源會發起一個跨域 HTTP 請求

     http://localhost:9000請求http://localhost:8761/eureka/apps就是違背了上述原則,即:請求伺服器不同埠的另一個資源,出於安全原因,瀏覽器限制發起的跨源HTTP請求,則會出現本文開頭提到的現象及異常。

     例如,XMLHttpRequest和Fetch API遵循同源策略, 這意味著使用這些API的Web應用程式只能從載入應用程式的同一個域請求HTTP資源,除非使用CORS頭。

      跨域資源共享( CORS )機制允許 Web 應用伺服器進行跨域訪問控制,從而使跨域資料傳輸得以安全進行。瀏覽器支援在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 請求所帶來的風險。


什麼情況下存在跨域問題

  • 本文提到的由 XMLHttpRequest 或 Fetch 發起的跨域 HTTP 請求。
  • Web 字型 (CSS 中通過 @font-face 使用跨域字型資源),,因此,網站就可以釋出 TrueType 字型資源,並只允許已授權網站進行跨站呼叫。
  • WebGL 貼圖。
  • 使用 drawImage 將 Images/video 畫面繪製到 canvas
  • 樣式表(使用 CSSOM)。

 

面對CORS的限制,將如何解決呢

     世間萬物完事,有因必有果,有果必有因。當然CORS的限制,官方也是給出瞭解決辦法的。

     CORS標準新增了一組 HTTP 頭欄位(Access-Control-Allow-Origin),允許伺服器宣告哪些源通過瀏覽器有許可權訪問哪些資源。另外,規範要求,對那些可能對伺服器資料產生副作用的 HTTP 請求方法(特別是 GET以外的 HTTP 請求,或者搭配某些 MIME 型別的 POST請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。伺服器確認允許之後,才發起實際的 HTTP 請求。在預檢請求的返回中,伺服器端也可以通知客戶端,是否需要攜帶身份憑證(包括Cookies 和 HTTP 認證相關資料)。

      CORS請求失敗會產生錯誤,但是為了安全,在JavaScript程式碼層面是無法獲知到底具體是哪裡出了問題。你只能檢視瀏覽器的控制檯以得知具體是哪裡出現了錯誤。

如果有興趣瞭解該機制剖析的可以參考https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

3、解決辦法

      在查閱大量資源,並瞭解過CORS機制後,解決辦法實質必定會圍繞Access-Control-Allow-Origin頭。

解決辦法如下:

新增響應頭

     在被請求資源中新增響應頭資訊"Access-Control-Allow-Origin:*

過濾器

    在本專案中新增如下過濾器:

/**
 * 解決跨域問題
 */
public class AccessControlAllowOriginFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Credentials", "true");

     chain.doFilter(req, response);
   } 

   public void init(FilterConfig filterConfig) {

   } 

   public void destroy() {

   } 

}

註解方式

      在Spring Boot中擁有大量的註解,針對跨域問題,也提供了對應的註解@CrossOrigin,使用方法如下:

import java.util.HashMap;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xcbeyond
 */
@RestController
@RequestMapping(value = "/api", method = RequestMethod.POST)
public class DemoController {
        
    @CrossOrigin(origins = "*")
    @RequestMapping(value = "/get")
    public String get() {
        ……
    }
}

 個人比較推薦使用上述的三種方式之一,其他方式請自己百度、谷歌吧

 

 

 

 

相關文章