web跨域问题CORS
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实现不同跨域