跨域問題(CORS / Access-Control-Allow-Origin)
作者:xcbeyond
瘋狂源自夢想,技術(shù)成就輝煌!微信公眾號:《程序猿技術(shù)大咖》號主,專注后端開發(fā)多年,擁有豐富的研發(fā)經(jīng)驗,樂于技術(shù)輸出、分享,現(xiàn)階段從事微服務(wù)架構(gòu)項目的研發(fā)工作,涉及架構(gòu)設(shè)計、技術(shù)選型、業(yè)務(wù)研發(fā)等工作。對于Java、微服務(wù)、數(shù)據(jù)庫、Docker有深入了解,并有大量的調(diào)優(yōu)經(jīng)驗。
1、前言
最近在項目中,調(diào)用Eureka REST接口時,出現(xiàn)了CORS跨越問題(Cross-origin resource sharing),在此與大家進行分享,避免多走些彎路。
項目前端(http://localhost:9000)通過Ajax方式調(diào)用Eureka REST 接口(http://localhost:8761/eureka/apps)時,卻沒有任何反應(yīng),則通過F12查看日志發(fā)現(xiàn)出現(xiàn)“Access-Control-Allow-Origin“類 異常,詳細如下:
…… http://localhost:8761/eureka/apps. Origin http://localhost:9000 is not allowed by Access-Control-Allow-Origin……
通過google,發(fā)現(xiàn)是由于CORS跨越問題造成的,解決辦法無非有兩種方式:響應(yīng)頭添加參數(shù)和添加過濾器,下面就詳細說說CORS跨越問題的起因與詳細解決辦法。
2、CORS
CORS,常被大家稱之為跨越問題,準(zhǔn)確的叫法是跨域資源共享(CORS,Cross-origin resource sharing),是W3C標(biāo)準(zhǔn),是一種機制,它使用額外的HTTP頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應(yīng)用被準(zhǔn)許訪問來自不同源服務(wù)器上的指定的資源。當(dāng)一個資源從與該資源本身所在的服務(wù)器不同的域或端口請求一個資源時,資源會發(fā)起一個跨域 HTTP 請求。
http://localhost:9000請求http://localhost:8761/eureka/apps就是違背了上述原則,即:請求服務(wù)器不同端口的另一個資源,出于安全原因,瀏覽器限制發(fā)起的跨源HTTP請求,則會出現(xiàn)本文開頭提到的現(xiàn)象及異常。
例如,XMLHttpRequest和Fetch API遵循同源策略, 這意味著使用這些API的Web應(yīng)用程序只能從加載應(yīng)用程序的同一個域請求HTTP資源,除非使用CORS頭。
跨域資源共享( CORS )機制允許 Web 應(yīng)用服務(wù)器進行跨域訪問控制,從而使跨域數(shù)據(jù)傳輸?shù)靡园踩M行。瀏覽器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 請求所帶來的風(fēng)險。
什么情況下存在跨域問題
本文提到的由 XMLHttpRequest 或 Fetch 發(fā)起的跨域 HTTP 請求。
Web 字體 (CSS 中通過 @font-face 使用跨域字體資源),,因此,網(wǎng)站就可以發(fā)布 TrueType 字體資源,并只允許已授權(quán)網(wǎng)站進行跨站調(diào)用。
WebGL 貼圖。
使用 drawImage 將 Images/video 畫面繪制到 canvas
樣式表(使用 CSSOM)。
面對CORS的限制,將如何解決呢
世間萬物完事,有因必有果,有果必有因。當(dāng)然CORS的限制,官方也是給出了解決辦法的。
CORS標(biāo)準(zhǔn)新增了一組 HTTP 頭字段(Access-Control-Allow-Origin),允許服務(wù)器聲明哪些源通過瀏覽器有權(quán)限訪問哪些資源。另外,規(guī)范要求,對那些可能對服務(wù)器數(shù)據(jù)產(chǎn)生副作用的 HTTP 請求方法(特別是 GET以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST請求),瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個預(yù)檢請求(preflight request),從而獲知服務(wù)端是否允許該跨域請求。服務(wù)器確認允許之后,才發(fā)起實際的 HTTP 請求。在預(yù)檢請求的返回中,服務(wù)器端也可以通知客戶端,是否需要攜帶身份憑證(包括Cookies 和 HTTP 認證相關(guān)數(shù)據(jù))。
CORS請求失敗會產(chǎn)生錯誤,但是為了安全,在JavaScript代碼層面是無法獲知到底具體是哪里出了問題。你只能查看瀏覽器的控制臺以得知具體是哪里出現(xiàn)了錯誤。
如果有興趣了解該機制剖析的可以參考https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
3、解決辦法
在查閱大量資源,并了解過CORS機制后,解決辦法實質(zhì)必定會圍繞Access-Control-Allow-Origin頭。
解決辦法如下:
添加響應(yīng)頭
在被請求資源中添加響應(yīng)頭信息"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中擁有大量的注解,針對跨域問題,也提供了對應(yīng)的注解@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() {
……
}
}
個人比較推薦使用上述的三種方式之一,其他方式請自己百度、谷歌吧