跨域資源共享(CORS)
- 同一個瀏覽器發出的請求,未必都是用戶自願發出的請求
- 比如瀏覽頁面時只有 page.html 是用戶發出的,其他請求是瀏覽器自動發出的
- 跨域資源共享(CORS) 使用額外的 HTTP 頭來告訴瀏覽器,讓運行在一個
origin
域上的Web應用被准許訪問來自不同源服務器上的指定的資源
- 瀏覽器本地緩存也會受到跨域限制
- cookie、storage、indexDB等
- 跨域指不同的域名,協議或端口
- 例如http和https就是不同協議,從http協議的地址向同一域名下https協議的地址請求會產生跨域
- 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與
- 瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求
- 因此,實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信
- 如果請求的Origin的值不在指定的許可範圍,服務端返回一個正常的HTTP迴應,但這個迴應的頭信息沒有包含Access-Control-Allow-Origin字段
- 此時瀏覽器就知道跨域請求失敗了
- 這種錯誤無法通過狀態碼識別,因爲HTTP迴應的狀態碼有可能是200
- 同源策略只是瀏覽器的一個安全策略,只適用於瀏覽器向服務器發送請求的時候
- 當服務器跟服務器發送請求的時候,自然就沒有這麼一層限制
跨域通信過程
- 對那些可能對服務器數據產生副作用的非簡單請求方法,瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求
- 從而獲知服務端是否允許該跨域請求,服務器確認允許之後,才發起實際的 HTTP 請求
- 簡單請求:
- 請求方法爲
HEAD
、GET
或者POST
中的一種
- HTTP的頭信息不超過以下幾種字段
Accept
、Accept-Language
、Content-Language
Content-Type
的值只限於application/x-www-form-urlencoded
、multipart/form-data
、text/plain
三個
- 經常使用的
application/json
就不是簡單請求
- 請求首部字段
Origin
表明該請求來源,即請求發起的域名
- 對於預檢請求的頭信息,必須包括兩個特殊字段
Access-Control-Request-Method
Access-Control-Request-Headers
- 一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段
- 響應首部字段
Access-Control-Allow-Origin
- 表明該資源被允許訪問的域名,只能是單個域名或者
*
- 逗號分隔多個域名是行不通
- 但可以先獲取請求origin進行判斷,判斷成功後把origin寫入該字段的方式規避限制
*
表明該資源可以被任意外域訪問
- 正確響應的值應當爲
*
或者包含由 Origin 首部字段所指明的域名
Access-Control-Allow-Headers
- 表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段
- 它也是一個逗號分隔的字符串,是必須的
Access-Control-Allow-Methods
-
- 表明服務器支持的所有跨域請求的方法, 同樣不限於瀏覽器在"預檢"中請求的字段
- 該字段必需,它的值是逗號分隔的一個字符串
Access-Control-Allow-Credentials
- 表明是否允許請求時攜帶Cookie
- 如果要發送Cookie,
Access-Control-Allow-Origin
就不能設爲星號,必須指定明確的、與請求網頁一致的域名
Access-Control-Max-Age
- 該字段可選,用來指定本次預檢請求的有效期
- 允許緩存該條迴應,在此期間,不用發出另一條預檢請求
go後端處理方式
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
if isValidOrigin(origin) {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH, DELETE")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, Expires")
c.Header("Access-Control-Allow-Credentials", "true")
method := c.Request.Method
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
}
c.Next()
}
}
參考