1. 程式人生 > 實用技巧 >網路程式設計-跨域資源共享 CORS

網路程式設計-跨域資源共享 CORS

1.什麼是同源策略?

如果兩個 URL 的 protocol、port 和 host 都相同的話,則這兩個 URL 是同源。同源策略對Web應用程式具有特殊意義,因為Web應用程式廣泛依賴於HTTP cookie來維持使用者會話,所以必須將不相關網站嚴格分隔,以防止丟失資料洩露。
值得注意的是同源策略僅適用於指令碼,這意味著某網站可以通過相應的HTML標籤訪問不同來源網站上的影象、CSS和動態載入指令碼等資源。而跨站請求偽造就是利用同源策略不適用於HTML標籤的缺陷。

2.跨域資源共享 CORS

跨域資源共享(英語:Cross-origin resource sharing,縮寫:CORS),用於讓網頁的受限資源能夠被其他域名的頁面訪問的一種機制。

通過該機制,頁面能夠自由地使用不同源(英語:cross-origin)的圖片、樣式、指令碼、iframes以及視訊。一些跨域的請求(特別是Ajax)常常會被同源策略所禁止的。跨源資源共享定義了一種方式,為的是瀏覽器和伺服器之間能互相確認是否足夠安全以至於能使用跨源請求。比起純粹的同源請求,這將更為自由和功能性的(functionality ),但比純粹的跨源請求更為安全。

3.預檢請求

需預檢的請求要求必須首先使用 OPTIONS 方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料產生未預期的影響。

不會觸發CORS預檢的請求稱為簡單請求,滿足以下所有條件的才會被視為簡單請求

1)使用GET、POST、HEAD其中一種方法

2)只使用瞭如下的安全首部欄位,不得人為設定其他首部欄位

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type 只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

4.CORS相關欄位

  • Access-Control-Allow-Origin:該欄位是必須的。它的值要麼是請求時Origin欄位的值,要麼是一個*,表示接受任意域名的請求。
  • Access-Control-Allow-Headers:表明伺服器允許請求中攜帶的頭資訊欄位。值為逗號分割的列表。
  • Access-Control-Allow-Credentials:該欄位可選。它的值是一個布林值,表示是否允許傳送Cookie。預設情況下,Cookie不包括在CORS請求之中。設為true,即表示伺服器明確許可,Cookie可以包含在請求中,一起發給伺服器。這個值也只能設為true,如果伺服器不要瀏覽器傳送Cookie,刪除該欄位即可。
  • Access-Control-Allow-Methods:該欄位必需,用於預檢請求的響應。其指明瞭實際請求所允許使用的 HTTP 方法。
  • Access-Control-Max-Age:該欄位可選,用來指定本次預檢請求的有效期,單位為秒。在此期間,不用發出另一條預檢請求。

5.Golang實現跨域

客戶端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>XMLHttpRequest CORS</title>
</head>
<body>
    <h2>XMLHttpRequest CORS</h2>

    <button id="send">傳送POST(application/x-www-form-urlencoded)</button>
    <button id="sendJSON">傳送POST(application/json)</button>
    <script type="application/javascript">
        document.getElementById('send').onclick = function (evt) {
            let xhr = new XMLHttpRequest();
            xhr.open("POST", "http://127.0.0.1:8080/index", true)
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
            xhr.setRequestHeader("x-Custome-Header", "true")

            let params = new URLSearchParams()
            params.append("name", "dazuo")
            params.append("age", "24")
            xhr.send(params)
        }

        document.getElementById('sendJSON').onclick = function (evt) {
            let xhr = new XMLHttpRequest();
            xhr.open("POST", "http://127.0.0.1:8080/index", true)
            xhr.setRequestHeader("Content-Type", "application/json")
            xhr.setRequestHeader("x-Custome-Header", "true")

            let postData = JSON.stringify({
                name: "dazuo",
                age: 24
            })
            xhr.send(postData)
        }
    </script>
</body>
</html>

服務端

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func HTTPCORS() {
	http.HandleFunc("/index", func(w http.ResponseWriter, r *http.Request) {
		header := w.Header()
		header.Set("Access-Control-Allow-Origin", "*")
		header.Set("Access-Control-Allow-Headers", "x-Custome-Header,Content-Type")
		header.Set("Access-Control-Allow-Credentials", "false")
		header.Set("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT,OPTIONS")
		header.Set("Access-Control-Max-Age", "5")
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusNoContent)
		}
		reqBody, _ := ioutil.ReadAll(r.Body)
		fmt.Printf("RequestURI: %s, Methods: %s, ReqBody: %s\n", r.RequestURI, r.Method, reqBody)
		_, _ = w.Write([]byte("welcome !"))
	})

	err := http.ListenAndServe("127.0.0.1:8080", nil)
	if err != nil {
		log.Panic(err)
	}
}

6.參考資料

1.同源策略 - 維基百科

2.跨域資源共享 - 維基百科

3.跨域資源共享 CORS 詳解 - 阮一峰的網路日誌