HTTP訪問控制(跨域問題)

跨域資源共享(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 請求
    • 簡單請求:
      • 請求方法爲HEADGET或者POST中的一種
      • HTTP的頭信息不超過以下幾種字段AcceptAccept-LanguageContent-Language
      • Content-Type的值只限於application/x-www-form-urlencodedmultipart/form-datatext/plain三個
        • 經常使用的application/json就不是簡單請求
  • 請求首部字段
    • Origin 表明該請求來源,即請求發起的域名
    • 對於預檢請求的頭信息,必須包括兩個特殊字段
      • Access-Control-Request-Method
        • 列出瀏覽器的CORS請求會用到哪些HTTP方法
      • 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不能設爲星號,必須指定明確的、與請求網頁一致的域名
        • 主要是爲了Cookie安全
    • 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")

			// handle預查詢
			method := c.Request.Method
			if method == "OPTIONS" {
				c.AbortWithStatus(http.StatusNoContent)
			}
		}

		// 處理請求
		c.Next()
	}
}

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章