跨域请求
什么是域?
协议+域名+端口号都相同才是同域(同源策略),而同源策略又分两种
- OM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
- XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。
CORS/跨资源共享
跨源资源共享 Cross-Origin Resource Sharing(CORS) 是一个新的 W3C 标准,它新增的一组HTTP首部字段,允许服务端其声明哪些源站有权限访问哪些资源。换言之,它允许浏览器向声明了 CORS 的跨域服务器,发出 XMLHttpReuest 请求,从而克服 Ajax 只能同源使用的限制。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求
- 请求方法为 HEAD、GET、POST中的一种。
- HTTP头信息不超过一下几种:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
对于简单请求,浏览器会自动在请求的头部添加一个 Origin
字段来说明本次请求来自哪个源(协议 + 域名 + 端口),服务端则通过这个值判断是否接收本次请求。如果 Origin
在许可范围内,则服务器返回的响应会多出几个头信息
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Content-Length
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
非简单请求
对服务器有特殊要求的请求,比如请求方法是 PUT 或 DELETE ,或者 Content-Type 字段的类型是 application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),预检请求其实就是我们常说的 OPTIONS 请求,表示这个请求是用来询问的。具体如下:
- 会首先发送一个OPTIONS类型的请求,该请求是测试类型的,又称之为options嗅探,同时会在header中带上origin,判断是否有跨域请求权限,
- 然后服务器相应Access-Control-Allow-Origin的值,该值会与浏览器的origin值进行匹配,如果能够匹配通过,则表示有跨域访问的权限。
- 跨域访问权限检查通过,会正式发送POST请求。
头信息里面,关键字段 Origin ,表示请求来自哪个源,除 Origin 字段,"预检"请求的头信息包括两个特殊字段:
-
Access-Control-Request-Method
用来列出浏览器的CORS请求会用到哪些HTTP方法(必需)
-
Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段
通过配置这些参数来处理跨域请求
func Cors(context *gin.Context) {
method := context.Request.Method
// 必须,接受指定域的请求,可以使用*不加以限制,但不安全
//context.Header("Access-Control-Allow-Origin", "*")
context.Header("Access-Control-Allow-Origin", context.GetHeader("Origin"))
fmt.Println(context.GetHeader("Origin"))
// 必须,设置服务器支持的所有跨域请求的方法
context.Header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
// 服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
context.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Token")
// 可选,设置XMLHttpRequest的响应对象能拿到的额外字段
context.Header("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token")
// 可选,是否允许后续请求携带认证信息Cookir,该值只能是true,不需要则不设置
context.Header("Access-Control-Allow-Credentials", "true")
// 放行所有OPTIONS方法
if method == "OPTIONS" {
context.AbortWithStatus(http.StatusNoContent)
return
}
context.Next()
}
* 常常使用在公开的无敏感信息的接口