1. 是什么?
同源策略
同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSRF等攻擊。
所謂同源是指"協議+域名+端口"三者相同,即便兩個不同的域名指向同一個ip地址,也非同源。
跨域 (cors)
cross-origin resource sharing:當協議、子域名、主域名、端口號中任意一個不相同時,都算作不同域。
不同域之間相互請求資源,就算作“跨域”。例如:
http://localhost:8080 -> http://localhost:8088
2. 為什么?
瀏覽器攔截
跨域並不是沒有發出請求,其實請求能發出去,服務端收到請求並正常返回結果,只是結果被瀏覽器攔截了。
瀏覽器為了阻止用戶讀取到另一個域名下的內容,攔截了ajax響應。
3. 怎樣解決?
CORS設置頭
只要服務器端設置相應的頭允許跨域即可。
Access-Control-Allow-Origin
根據Reuqest請求頭中的Origin來判斷該請求的資源是否可以被共享。
如果Origin指定的源,不在許可范圍內,服務器會返回一個正常的HTTP回應。瀏覽器發現,這個回應的頭信息沒有包含Access-Control-Allow-Origin字段(該字段的值為服務端設置Access-Control-Allow-Origin的值)便知出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲。此時HTTP的返回碼為200,所以 這種錯誤無法通過狀態碼識別。
Access-Control-Allow-Credentials
指定是否允許請求帶上cookies,HTTP authentication,client-side SSL certificates等消息。
如需要帶上這些信息,Access-Control-Allow-Credentials:true
並需要在XmlHpptRequest中設置xhr.withCredentials=true
。
需注意的是,當設置了the credentials flag為true,那么Access-Control-Allow-Origin就不能使用"*
"
Access-Control-Max-Age
可選字段,指定了一個預請求將緩存多久,在緩存失效前將不會再發送預請求。
Access-Control-Allow-Methods
作為預請求Response的一部分,指定了真實請求可以使用的請求方式。
Access-Control-Allow-Headers
作為預請求Response的一部分,指定了真實請求可以使用的請求頭名稱(header field names)。
golang實現
CORS
需要瀏覽器和服務器同時支持。整個CORS
通信過程,瀏覽器是自動完成,而服務器需要手動配置。
golang
設置HTTP
頭部相當簡單,標准包有現成的方法可以使用。
//https://gitee.com/yuxio/epcforedge.git "github.com/gorilla/handlers" "github.com/gorilla/mux" "golang.org/x/net/http2" headersOK := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}) originsOK := handlers.AllowedOrigins( []string{AfCtx.cfg.SrvCfg.UIEndpoint}) methodsOK := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "PATCH", "OPTIONS", "DELETE"}) AfRouter = NewAFRouter(AfCtx) serverCNCA := &http.Server{ Addr: AfCtx.cfg.SrvCfg.CNCAEndpoint, Handler: handlers.CORS(headersOK, originsOK, methodsOK)(AfRouter), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, }
示例
ajax.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script> function loadXMLDoc() { var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { document.getElementById("myDiv").innerHTML = xmlhttp.responseText; } } xmlhttp.open("GET", "http://127.0.0.1:9000", true); xmlhttp.send(); } </script> <title>Document</title> </head> <body> <h2>cross origin</h2> <button type="button" onclick="loadXMLDoc()">請求數據</button> <div id="myDiv"></div> </body> </html>
8000port.go
package main import ( "net/http" "html/template" ) func main(){ http.HandleFunc("/", Entrance) http.ListenAndServe(":8000", nil) } func Entrance(w http.ResponseWriter, r *http.Request){ t, _ := template.ParseFiles("ajax.html") t.Execute(w, nil) }
9000port.go
package main import ( "net/http" "fmt" ) func main(){ http.HandleFunc("/", TestCrossOrigin) http.ListenAndServe(":9000", nil) } func TestCrossOrigin(w http.ResponseWriter, r *http.Request){ w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, "hello cros!") }
如果注釋掉Access-Control-Allow-Origin,不能跨域訪問,報如下錯誤(console):
Access to XMLHttpRequest at 'http://127.0.0.1:9000/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
response Headers:
參考:
1. 跨域問題及解決方法 nginx部署 sinksmell https://github.com/sinksmell/lanblog.git
2. golang跨域訪問
3. Web開發之跨域與跨域資源共享 jsonp cros
4. 前端常見跨域解決方案(全) jsonp cors nginx nodejs websocket
5. ajax 廖雪峰
6. 跨域資源共享 CORS 詳解 阮一峰
7. golang server 跨域解決總結 動態許可跨域,根據不同url實現不同跨域